From 7b0ceee57bae04b6f60f0e6a0218f8ac5cdea4ea Mon Sep 17 00:00:00 2001 From: Steven Date: Sat, 16 Dec 2023 09:23:45 +0800 Subject: [PATCH] chore: update memo metadata description --- api/v1/rss.go | 4 +- plugin/gomark/render/renderer.go | 13 ++ plugin/gomark/render/string/string.go | 169 +++++++++++++++++++++ plugin/gomark/render/string/string_test.go | 34 +++++ server/frontend/frontend.go | 16 +- 5 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 plugin/gomark/render/string/string.go create mode 100644 plugin/gomark/render/string/string_test.go diff --git a/api/v1/rss.go b/api/v1/rss.go index 33c225c2..ed6c8270 100644 --- a/api/v1/rss.go +++ b/api/v1/rss.go @@ -15,7 +15,7 @@ import ( "github.com/usememos/memos/internal/util" "github.com/usememos/memos/plugin/gomark/parser" "github.com/usememos/memos/plugin/gomark/parser/tokenizer" - "github.com/usememos/memos/plugin/gomark/render/html" + "github.com/usememos/memos/plugin/gomark/render" "github.com/usememos/memos/store" ) @@ -198,7 +198,7 @@ func getRSSItemDescription(content string) (string, error) { if err != nil { return "", err } - result := html.NewHTMLRender().Render(nodes) + result := render.NewHTMLRender().Render(nodes) return result, nil } diff --git a/plugin/gomark/render/renderer.go b/plugin/gomark/render/renderer.go index da4cad77..065236e1 100644 --- a/plugin/gomark/render/renderer.go +++ b/plugin/gomark/render/renderer.go @@ -1 +1,14 @@ package render + +import ( + htmlrender "github.com/usememos/memos/plugin/gomark/render/html" + stringrender "github.com/usememos/memos/plugin/gomark/render/string" +) + +func NewHTMLRender() *htmlrender.HTMLRender { + return htmlrender.NewHTMLRender() +} + +func NewStringRender() *stringrender.StringRender { + return stringrender.NewStringRender() +} diff --git a/plugin/gomark/render/string/string.go b/plugin/gomark/render/string/string.go new file mode 100644 index 00000000..72f817d2 --- /dev/null +++ b/plugin/gomark/render/string/string.go @@ -0,0 +1,169 @@ +package string + +import ( + "bytes" + + "github.com/usememos/memos/plugin/gomark/ast" +) + +// StringRender renders AST to raw string. +type StringRender struct { + output *bytes.Buffer + context *RendererContext +} + +type RendererContext struct { +} + +// NewStringRender creates a new StringRender. +func NewStringRender() *StringRender { + return &StringRender{ + output: new(bytes.Buffer), + context: &RendererContext{}, + } +} + +// RenderNode renders a single AST node to raw string. +func (r *StringRender) RenderNode(node ast.Node) { + switch n := node.(type) { + case *ast.LineBreak: + r.renderLineBreak(n) + case *ast.Paragraph: + r.renderParagraph(n) + case *ast.CodeBlock: + r.renderCodeBlock(n) + case *ast.Heading: + r.renderHeading(n) + case *ast.HorizontalRule: + r.renderHorizontalRule(n) + case *ast.Blockquote: + r.renderBlockquote(n) + case *ast.UnorderedList: + r.renderUnorderedList(n) + case *ast.OrderedList: + r.renderOrderedList(n) + case *ast.Bold: + r.renderBold(n) + case *ast.Italic: + r.renderItalic(n) + case *ast.BoldItalic: + r.renderBoldItalic(n) + case *ast.Code: + r.renderCode(n) + case *ast.Image: + r.renderImage(n) + case *ast.Link: + r.renderLink(n) + case *ast.Tag: + r.renderTag(n) + case *ast.Strikethrough: + r.renderStrikethrough(n) + case *ast.Text: + r.renderText(n) + default: + // Handle other block types if needed. + } +} + +// RenderNodes renders a slice of AST nodes to raw string. +func (r *StringRender) RenderNodes(nodes []ast.Node) { + for _, node := range nodes { + r.RenderNode(node) + } +} + +// Render renders the AST to raw string. +func (r *StringRender) Render(astRoot []ast.Node) string { + r.RenderNodes(astRoot) + return r.output.String() +} + +func (r *StringRender) renderLineBreak(_ *ast.LineBreak) { + r.output.WriteString("\n") +} + +func (r *StringRender) renderParagraph(node *ast.Paragraph) { + r.RenderNodes(node.Children) +} + +func (r *StringRender) renderCodeBlock(node *ast.CodeBlock) { + r.output.WriteString(node.Content) +} + +func (r *StringRender) renderHeading(node *ast.Heading) { + r.RenderNodes(node.Children) + r.output.WriteString("\n") +} + +func (r *StringRender) renderHorizontalRule(_ *ast.HorizontalRule) { + r.output.WriteString("\n---\n") +} + +func (r *StringRender) renderBlockquote(node *ast.Blockquote) { + r.output.WriteString("\n") + r.RenderNodes(node.Children) + r.output.WriteString("\n") +} + +func (r *StringRender) renderUnorderedList(node *ast.UnorderedList) { + prevSibling, nextSibling := node.PrevSibling(), node.NextSibling() + if prevSibling == nil || prevSibling.Type() != ast.UnorderedListNode { + r.output.WriteString("\n") + } + r.output.WriteString("* ") + r.RenderNodes(node.Children) + if nextSibling == nil || nextSibling.Type() != ast.UnorderedListNode { + r.output.WriteString("\n") + } +} + +func (r *StringRender) renderOrderedList(node *ast.OrderedList) { + prevSibling, nextSibling := node.PrevSibling(), node.NextSibling() + if prevSibling == nil || prevSibling.Type() != ast.OrderedListNode { + r.output.WriteString("\n") + } + r.output.WriteString("1. ") + r.RenderNodes(node.Children) + if nextSibling == nil || nextSibling.Type() != ast.OrderedListNode { + r.output.WriteString("\n") + } +} + +func (r *StringRender) renderText(node *ast.Text) { + r.output.WriteString(node.Content) +} + +func (r *StringRender) renderBold(node *ast.Bold) { + r.output.WriteString(node.Content) +} + +func (r *StringRender) renderItalic(node *ast.Italic) { + r.output.WriteString(node.Content) +} + +func (r *StringRender) renderBoldItalic(node *ast.BoldItalic) { + r.output.WriteString(node.Content) +} + +func (r *StringRender) renderCode(node *ast.Code) { + r.output.WriteString("`") + r.output.WriteString(node.Content) + r.output.WriteString("`") +} + +func (*StringRender) renderImage(*ast.Image) { + // Do nothing. +} + +func (*StringRender) renderLink(*ast.Link) { + // Do nothing. +} + +func (r *StringRender) renderTag(node *ast.Tag) { + r.output.WriteString(`#`) + r.output.WriteString(node.Content) +} + +func (r *StringRender) renderStrikethrough(node *ast.Strikethrough) { + r.output.WriteString(node.Content) +} diff --git a/plugin/gomark/render/string/string_test.go b/plugin/gomark/render/string/string_test.go new file mode 100644 index 00000000..e80f8c63 --- /dev/null +++ b/plugin/gomark/render/string/string_test.go @@ -0,0 +1,34 @@ +package string + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/usememos/memos/plugin/gomark/parser" + "github.com/usememos/memos/plugin/gomark/parser/tokenizer" +) + +func TestStringRender(t *testing.T) { + tests := []struct { + text string + expected string + }{ + { + text: "Hello world!", + expected: `Hello world!`, + }, + { + text: "**Hello** world!", + expected: `Hello world!`, + }, + } + + for _, test := range tests { + tokens := tokenizer.Tokenize(test.text) + nodes, err := parser.Parse(tokens) + require.NoError(t, err) + actual := NewStringRender().Render(nodes) + require.Equal(t, test.expected, actual) + } +} diff --git a/server/frontend/frontend.go b/server/frontend/frontend.go index 039125f4..d1165dc5 100644 --- a/server/frontend/frontend.go +++ b/server/frontend/frontend.go @@ -12,6 +12,9 @@ import ( v1 "github.com/usememos/memos/api/v1" "github.com/usememos/memos/internal/util" + "github.com/usememos/memos/plugin/gomark/parser" + "github.com/usememos/memos/plugin/gomark/parser/tokenizer" + "github.com/usememos/memos/plugin/gomark/render" "github.com/usememos/memos/server/profile" "github.com/usememos/memos/store" ) @@ -151,12 +154,23 @@ Sitemap: %s/sitemap.xml`, instanceURL, instanceURL) } func generateMemoMetadata(memo *store.Memo, creator *store.User) string { - description := memo.Content + description := "" if memo.Visibility == store.Private { description = "This memo is private." } else if memo.Visibility == store.Protected { description = "This memo is protected." + } else { + tokens := tokenizer.Tokenize(memo.Content) + nodes, _ := parser.Parse(tokens) + description = render.NewStringRender().Render(nodes) + if len(description) == 0 { + description = memo.Content + } + if len(description) > 100 { + description = description[:100] + "..." + } } + metadataList := []string{ fmt.Sprintf(``, description), fmt.Sprintf(``, fmt.Sprintf("%s(@%s) on Memos", creator.Nickname, creator.Username)),