Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(examples): add p/moul/template #3299

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 34 additions & 24 deletions examples/gno.land/p/moul/md/md.gno
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ import (
// Bold returns bold text for markdown.
// Example: Bold("foo") => "**foo**"
func Bold(text string) string {
return "**" + text + "**"
return "**" + strings.TrimSpace(text) + "**"
}

// Italic returns italicized text for markdown.
// Example: Italic("foo") => "*foo*"
func Italic(text string) string {
return "*" + text + "*"
return "*" + strings.TrimSpace(text) + "*"
}

// Strikethrough returns strikethrough text for markdown.
Expand All @@ -35,37 +35,37 @@ func Strikethrough(text string) string {
// H1 returns a level 1 header for markdown.
// Example: H1("foo") => "# foo\n"
func H1(text string) string {
return "# " + text + "\n"
return "# " + strings.TrimSpace(text)
}

// H2 returns a level 2 header for markdown.
// Example: H2("foo") => "## foo\n"
func H2(text string) string {
return "## " + text + "\n"
return "## " + strings.TrimSpace(text)
}

// H3 returns a level 3 header for markdown.
// Example: H3("foo") => "### foo\n"
func H3(text string) string {
return "### " + text + "\n"
return "### " + strings.TrimSpace(text)
}

// H4 returns a level 4 header for markdown.
// Example: H4("foo") => "#### foo\n"
func H4(text string) string {
return "#### " + text + "\n"
return "#### " + strings.TrimSpace(text)
}

// H5 returns a level 5 header for markdown.
// Example: H5("foo") => "##### foo\n"
func H5(text string) string {
return "##### " + text + "\n"
return "##### " + strings.TrimSpace(text)
}

// H6 returns a level 6 header for markdown.
// Example: H6("foo") => "###### foo\n"
func H6(text string) string {
return "###### " + text + "\n"
return "###### " + strings.TrimSpace(text)
}

// BulletList returns a bullet list for markdown.
Expand All @@ -83,9 +83,9 @@ func BulletList(items []string) string {
func BulletItem(item string) string {
var sb strings.Builder
lines := strings.Split(item, "\n")
sb.WriteString("- " + lines[0] + "\n")
sb.WriteString("- " + strings.TrimSpace(lines[0]) + "\n")
for _, line := range lines[1:] {
sb.WriteString(" " + line + "\n")
sb.WriteString(" " + strings.TrimSpace(line) + "\n")
}
return sb.String()
}
Expand All @@ -96,9 +96,9 @@ func OrderedList(items []string) string {
var sb strings.Builder
for i, item := range items {
lines := strings.Split(item, "\n")
sb.WriteString(strconv.Itoa(i+1) + ". " + lines[0] + "\n")
sb.WriteString(strconv.Itoa(i+1) + ". " + strings.TrimSpace(lines[0]) + "\n")
for _, line := range lines[1:] {
sb.WriteString(" " + line + "\n")
sb.WriteString(" " + strings.TrimSpace(line) + "\n")
}
}
return sb.String()
Expand All @@ -123,19 +123,19 @@ func TodoItem(item string, done bool) string {
checkbox = "x"
}
lines := strings.Split(item, "\n")
sb.WriteString("- [" + checkbox + "] " + lines[0] + "\n")
sb.WriteString("- [" + checkbox + "] " + strings.TrimSpace(lines[0]) + "\n")
for _, line := range lines[1:] {
sb.WriteString(" " + line + "\n")
sb.WriteString(" " + strings.TrimSpace(line) + "\n")
}
return sb.String()
}

// Nested prefixes each line with a given prefix, enabling nested lists.
// Example: Nested("- foo\n- bar", " ") => " - foo\n - bar\n"
// Nested prefixes each line with a given prefix, preserving existing spaces.
// Example: Nested("- foo\n- bar", " ") => " - foo\n - bar"
func Nested(content, prefix string) string {
lines := strings.Split(content, "\n")
for i := range lines {
if strings.TrimSpace(lines[i]) != "" {
if lines[i] != "" {
lines[i] = prefix + lines[i]
}
}
Expand All @@ -145,10 +145,13 @@ func Nested(content, prefix string) string {
// Blockquote returns a blockquote for markdown.
// Example: Blockquote("foo\nbar") => "> foo\n> bar\n"
func Blockquote(text string) string {
lines := strings.Split(text, "\n")
lines := strings.Split(strings.TrimSpace(text), "\n")
var sb strings.Builder
for _, line := range lines {
sb.WriteString("> " + line + "\n")
for i, line := range lines {
sb.WriteString("> " + strings.TrimSpace(line))
if i < len(lines)-1 {
sb.WriteString("\n")
}
}
return sb.String()
}
Expand Down Expand Up @@ -180,7 +183,7 @@ func HorizontalRule() string {
// Link returns a hyperlink for markdown.
// Example: Link("foo", "http://example.com") => "[foo](http://example.com)"
func Link(text, url string) string {
return "[" + EscapeText(text) + "](" + url + ")"
return "[" + strings.TrimSpace(text) + "](" + url + ")"
}

// InlineImageWithLink creates an inline image wrapped in a hyperlink for markdown.
Expand All @@ -192,7 +195,7 @@ func InlineImageWithLink(altText, imageUrl, linkUrl string) string {
// Image returns an image for markdown.
// Example: Image("foo", "http://example.com") => "![foo](http://example.com)"
func Image(altText, url string) string {
return "![" + EscapeText(altText) + "](" + url + ")"
return "![" + strings.TrimSpace(altText) + "](" + url + ")"
}

// Footnote returns a footnote for markdown.
Expand All @@ -202,9 +205,16 @@ func Footnote(reference, text string) string {
}

// Paragraph wraps the given text in a Markdown paragraph.
// Example: Paragraph("foo") => "foo\n"
// Preserves indentation for multiline content
func Paragraph(content string) string {
return content + "\n\n"
lines := strings.Split(content, "\n")
// Trim first line
lines[0] = strings.TrimSpace(lines[0])
// For subsequent lines, only trim right space but preserve left indentation
for i := 1; i < len(lines); i++ {
lines[i] = strings.TrimRight(lines[i], " \t")
}
return strings.Join(lines, "\n")
}

// CollapsibleSection creates a collapsible section for markdown using
Expand Down
158 changes: 98 additions & 60 deletions examples/gno.land/p/moul/md/md_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -3,86 +3,124 @@ package md
import (
"testing"

"gno.land/p/moul/md"
"gno.land/p/demo/uassert"
)

func TestHelpers(t *testing.T) {
tests := []struct {
name string
function func() string
fn func() string
expected string
}{
{"Bold", func() string { return md.Bold("foo") }, "**foo**"},
{"Italic", func() string { return md.Italic("foo") }, "*foo*"},
{"Strikethrough", func() string { return md.Strikethrough("foo") }, "~~foo~~"},
{"H1", func() string { return md.H1("foo") }, "# foo\n"},
{"HorizontalRule", md.HorizontalRule, "---\n"},
{"InlineCode", func() string { return md.InlineCode("foo") }, "`foo`"},
{"CodeBlock", func() string { return md.CodeBlock("foo") }, "```\nfoo\n```"},
{"LanguageCodeBlock", func() string { return md.LanguageCodeBlock("go", "foo") }, "```go\nfoo\n```"},
{"Link", func() string { return md.Link("foo", "http://example.com") }, "[foo](http://example.com)"},
{"Image", func() string { return md.Image("foo", "http://example.com") }, "![foo](http://example.com)"},
{"InlineImageWithLink", func() string { return md.InlineImageWithLink("alt", "image-url", "link-url") }, "[![alt](image-url)](link-url)"},
{"Footnote", func() string { return md.Footnote("foo", "bar") }, "[foo]: bar"},
{"Paragraph", func() string { return md.Paragraph("foo") }, "foo\n\n"},
// Basic formatting
{"Bold", func() string { return Bold("foo") }, "**foo**"},
{"Bold with spaces", func() string { return Bold(" foo ") }, "**foo**"},
{"Italic", func() string { return Italic("foo") }, "*foo*"},
{"Italic with spaces", func() string { return Italic(" foo ") }, "*foo*"},
{"Strikethrough", func() string { return Strikethrough("foo") }, "~~foo~~"},

// Headers
{"H1", func() string { return H1("foo") }, "# foo"},
{"H2", func() string { return H2("foo") }, "## foo"},
{"H3", func() string { return H3("foo") }, "### foo"},
{"H4", func() string { return H4("foo") }, "#### foo"},
{"H5", func() string { return H5("foo") }, "##### foo"},
{"H6", func() string { return H6("foo") }, "###### foo"},

// Headers with spaces
{"H1 with spaces", func() string { return H1(" foo ") }, "# foo"},
{"H2 with spaces", func() string { return H2(" foo ") }, "## foo"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.function()
if result != tt.expected {
t.Errorf("%s() = %q, want %q", tt.name, result, tt.expected)
}
got := tt.fn()
uassert.Equal(t, tt.expected, got)
})
}
}

func TestLists(t *testing.T) {
t.Run("BulletList", func(t *testing.T) {
items := []string{"foo", "bar"}
expected := "- foo\n- bar\n"
result := md.BulletList(items)
if result != expected {
t.Errorf("BulletList(%q) = %q, want %q", items, result, expected)
}
})
tests := []struct {
name string
fn func() string
expected string
}{
{"BulletItem", func() string { return BulletItem("foo") }, "- foo\n"},
{"BulletItem with spaces", func() string { return BulletItem(" foo ") }, "- foo\n"},
{"BulletItem multiline", func() string { return BulletItem(" foo \n bar ") }, "- foo\n bar\n"},
{"BulletList", func() string { return BulletList([]string{"foo", "bar"}) }, "- foo\n- bar\n"},
{"BulletList with spaces", func() string { return BulletList([]string{" foo ", " bar "}) }, "- foo\n- bar\n"},
{"OrderedList", func() string { return OrderedList([]string{"foo", "bar"}) }, "1. foo\n2. bar\n"},
{"OrderedList with spaces", func() string { return OrderedList([]string{" foo ", " bar "}) }, "1. foo\n2. bar\n"},
{"TodoList", func() string { return TodoList([]string{"foo", "bar"}, []bool{true, false}) }, "- [x] foo\n- [ ] bar\n"},
{"TodoList with spaces", func() string { return TodoList([]string{" foo ", " bar \n more "}, []bool{true, false}) }, "- [x] foo\n- [ ] bar\n more\n"},
}

t.Run("OrderedList", func(t *testing.T) {
items := []string{"foo", "bar"}
expected := "1. foo\n2. bar\n"
result := md.OrderedList(items)
if result != expected {
t.Errorf("OrderedList(%q) = %q, want %q", items, result, expected)
}
})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.fn()
uassert.Equal(t, tt.expected, got)
})
}
}

t.Run("TodoList", func(t *testing.T) {
items := []string{"foo", "bar\nmore bar"}
done := []bool{true, false}
expected := "- [x] foo\n- [ ] bar\n more bar\n"
result := md.TodoList(items, done)
if result != expected {
t.Errorf("TodoList(%q, %q) = %q, want %q", items, done, result, expected)
}
})
func TestFormatting(t *testing.T) {
tests := []struct {
name string
fn func() string
expected string
}{
{"Blockquote", func() string { return Blockquote("foo") }, "> foo"},
{"Blockquote with spaces", func() string { return Blockquote(" foo \n bar ") }, "> foo\n> bar"},
{"Link", func() string { return Link("foo", "http://example.com") }, "[foo](http://example.com)"},
{"Link with spaces", func() string { return Link(" foo ", "http://example.com") }, "[foo](http://example.com)"},
{"Image", func() string { return Image("foo", "http://example.com") }, "![foo](http://example.com)"},
{"Image with spaces", func() string { return Image(" foo ", "http://example.com") }, "![foo](http://example.com)"},
{"Paragraph", func() string { return Paragraph("foo") }, "foo"},
{"Paragraph with spaces", func() string { return Paragraph(" foo ") }, "foo"},
{"Paragraph multiline", func() string { return Paragraph(" foo \n bar ") }, "foo\n bar"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.fn()
uassert.Equal(t, tt.expected, got)
})
}
}

func TestNested(t *testing.T) {
t.Run("Nested Single Level", func(t *testing.T) {
content := "- foo\n- bar"
expected := " - foo\n - bar"
result := md.Nested(content, " ")
if result != expected {
t.Errorf("Nested(%q) = %q, want %q", content, result, expected)
}
})
tests := []struct {
name string
content string
prefix string
expected string
}{
{
name: "basic nesting",
content: "- foo\n- bar",
prefix: " ",
expected: " - foo\n - bar",
},
{
name: "preserve spaces",
content: " - foo \n - bar ",
prefix: " ",
expected: " - foo \n - bar ",
},
{
name: "empty lines",
content: "- foo\n\n- bar",
prefix: " ",
expected: " - foo\n\n - bar",
},
}

t.Run("Nested Double Level", func(t *testing.T) {
content := " - foo\n - bar"
expected := " - foo\n - bar"
result := md.Nested(content, " ")
if result != expected {
t.Errorf("Nested(%q) = %q, want %q", content, result, expected)
}
})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Nested(tt.content, tt.prefix)
uassert.Equal(t, tt.expected, got)
})
}
}
1 change: 1 addition & 0 deletions examples/gno.land/p/moul/template/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/p/moul/template
Loading
Loading