Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
6cb96ef6
Commit
6cb96ef6
authored
Oct 26, 2025
by
Claude
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: add missing punctuation in comments
parent
739fd2cd
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
48 additions
and
66 deletions
+48
-66
tag.go
plugin/markdown/extensions/tag.go
+1
-1
wikilink.go
plugin/markdown/extensions/wikilink.go
+1
-1
markdown.go
plugin/markdown/markdown.go
+28
-30
markdown_test.go
plugin/markdown/markdown_test.go
+1
-1
tag.go
plugin/markdown/parser/tag.go
+3
-3
tag_test.go
plugin/markdown/parser/tag_test.go
+6
-3
wikilink.go
plugin/markdown/parser/wikilink.go
+5
-5
wikilink_test.go
plugin/markdown/parser/wikilink_test.go
+2
-1
markdown_renderer.go
plugin/markdown/renderer/markdown_renderer.go
+1
-1
memo_service.go
server/router/api/v1/memo_service.go
+0
-20
No files found.
plugin/markdown/extensions/tag.go
View file @
6cb96ef6
...
...
@@ -10,7 +10,7 @@ import (
type
tagExtension
struct
{}
// TagExtension is a goldmark extension for #tag syntax
// TagExtension is a goldmark extension for #tag syntax
.
var
TagExtension
=
&
tagExtension
{}
// Extend extends the goldmark parser with tag support.
...
...
plugin/markdown/extensions/wikilink.go
View file @
6cb96ef6
...
...
@@ -10,7 +10,7 @@ import (
type
wikilinkExtension
struct
{}
// WikilinkExtension is a goldmark extension for [[...]] wikilink syntax
// WikilinkExtension is a goldmark extension for [[...]] wikilink syntax
.
var
WikilinkExtension
=
&
wikilinkExtension
{}
// Extend extends the goldmark parser with wikilink support.
...
...
plugin/markdown/markdown.go
View file @
6cb96ef6
...
...
@@ -17,7 +17,7 @@ import (
storepb
"github.com/usememos/memos/proto/gen/store"
)
// ExtractedData contains all metadata extracted from markdown in a single pass
// ExtractedData contains all metadata extracted from markdown in a single pass
.
type
ExtractedData
struct
{
Tags
[]
string
Property
*
storepb
.
MemoPayload_Property
...
...
@@ -57,12 +57,12 @@ type Service interface {
RenameTag
(
content
[]
byte
,
oldTag
,
newTag
string
)
(
string
,
error
)
}
// service implements the Service interface
// service implements the Service interface
.
type
service
struct
{
md
goldmark
.
Markdown
}
// Option configures the markdown service
// Option configures the markdown service
.
type
Option
func
(
*
config
)
type
config
struct
{
...
...
@@ -70,21 +70,21 @@ type config struct {
enableWikilink
bool
}
// WithTagExtension enables #tag parsing
// WithTagExtension enables #tag parsing
.
func
WithTagExtension
()
Option
{
return
func
(
c
*
config
)
{
c
.
enableTags
=
true
}
}
// WithWikilinkExtension enables [[wikilink]] parsing
// WithWikilinkExtension enables [[wikilink]] parsing
.
func
WithWikilinkExtension
()
Option
{
return
func
(
c
*
config
)
{
c
.
enableWikilink
=
true
}
}
// NewService creates a new markdown service with the given options
// NewService creates a new markdown service with the given options
.
func
NewService
(
opts
...
Option
)
Service
{
cfg
:=
&
config
{}
for
_
,
opt
:=
range
opts
{
...
...
@@ -115,14 +115,14 @@ func NewService(opts ...Option) Service {
}
}
// parse is an internal helper to parse content into AST
// parse is an internal helper to parse content into AST
.
func
(
s
*
service
)
parse
(
content
[]
byte
)
(
gast
.
Node
,
error
)
{
reader
:=
text
.
NewReader
(
content
)
doc
:=
s
.
md
.
Parser
()
.
Parse
(
reader
)
return
doc
,
nil
}
// ExtractTags returns all #tags found in content
// ExtractTags returns all #tags found in content
.
func
(
s
*
service
)
ExtractTags
(
content
[]
byte
)
([]
string
,
error
)
{
root
,
err
:=
s
.
parse
(
content
)
if
err
!=
nil
{
...
...
@@ -153,7 +153,7 @@ func (s *service) ExtractTags(content []byte) ([]string, error) {
return
uniqueLowercase
(
tags
),
nil
}
// ExtractProperties computes boolean properties about the content
// ExtractProperties computes boolean properties about the content
.
func
(
s
*
service
)
ExtractProperties
(
content
[]
byte
)
(
*
storepb
.
MemoPayload_Property
,
error
)
{
root
,
err
:=
s
.
parse
(
content
)
if
err
!=
nil
{
...
...
@@ -171,15 +171,9 @@ func (s *service) ExtractProperties(content []byte) (*storepb.MemoPayload_Proper
case
gast
.
KindLink
,
mast
.
KindWikilink
:
prop
.
HasLink
=
true
case
mast
.
KindWikilink
:
prop
.
HasLink
=
true
case
gast
.
KindCodeBlock
,
gast
.
KindFencedCodeBlock
,
gast
.
KindCodeSpan
:
prop
.
HasCode
=
true
case
gast
.
KindCodeSpan
:
prop
.
HasCode
=
true
case
east
.
KindTaskCheckBox
:
prop
.
HasTaskList
=
true
if
checkBox
,
ok
:=
n
.
(
*
east
.
TaskCheckBox
);
ok
{
...
...
@@ -187,6 +181,8 @@ func (s *service) ExtractProperties(content []byte) (*storepb.MemoPayload_Proper
prop
.
HasIncompleteTasks
=
true
}
}
default
:
// No special handling for other node types
}
return
gast
.
WalkContinue
,
nil
...
...
@@ -199,7 +195,7 @@ func (s *service) ExtractProperties(content []byte) (*storepb.MemoPayload_Proper
return
prop
,
nil
}
// ExtractReferences returns all wikilink references found in content
// ExtractReferences returns all wikilink references found in content
.
func
(
s
*
service
)
ExtractReferences
(
content
[]
byte
)
([]
string
,
error
)
{
root
,
err
:=
s
.
parse
(
content
)
if
err
!=
nil
{
...
...
@@ -229,7 +225,7 @@ func (s *service) ExtractReferences(content []byte) ([]string, error) {
return
references
,
nil
}
// RenderMarkdown renders goldmark AST back to markdown text
// RenderMarkdown renders goldmark AST back to markdown text
.
func
(
s
*
service
)
RenderMarkdown
(
content
[]
byte
)
(
string
,
error
)
{
root
,
err
:=
s
.
parse
(
content
)
if
err
!=
nil
{
...
...
@@ -240,7 +236,7 @@ func (s *service) RenderMarkdown(content []byte) (string, error) {
return
mdRenderer
.
Render
(
root
,
content
),
nil
}
// RenderHTML renders markdown content to HTML using goldmark's built-in HTML renderer
// RenderHTML renders markdown content to HTML using goldmark's built-in HTML renderer
.
func
(
s
*
service
)
RenderHTML
(
content
[]
byte
)
(
string
,
error
)
{
var
buf
bytes
.
Buffer
if
err
:=
s
.
md
.
Convert
(
content
,
&
buf
);
err
!=
nil
{
...
...
@@ -249,7 +245,7 @@ func (s *service) RenderHTML(content []byte) (string, error) {
return
buf
.
String
(),
nil
}
// GenerateSnippet creates a plain text summary from markdown content
// GenerateSnippet creates a plain text summary from markdown content
.
func
(
s
*
service
)
GenerateSnippet
(
content
[]
byte
,
maxLength
int
)
(
string
,
error
)
{
root
,
err
:=
s
.
parse
(
content
)
if
err
!=
nil
{
...
...
@@ -265,6 +261,8 @@ func (s *service) GenerateSnippet(content []byte, maxLength int) (string, error)
switch
n
.
Kind
()
{
case
gast
.
KindCodeBlock
,
gast
.
KindFencedCodeBlock
,
gast
.
KindCodeSpan
:
return
gast
.
WalkSkipChildren
,
nil
default
:
// Continue walking for other node types
}
// Add space before block elements (except first)
...
...
@@ -273,6 +271,8 @@ func (s *service) GenerateSnippet(content []byte, maxLength int) (string, error)
if
buf
.
Len
()
>
0
&&
lastNodeWasBlock
{
buf
.
WriteByte
(
' '
)
}
default
:
// No space needed for other node types
}
}
...
...
@@ -281,6 +281,8 @@ func (s *service) GenerateSnippet(content []byte, maxLength int) (string, error)
switch
n
.
Kind
()
{
case
gast
.
KindParagraph
,
gast
.
KindHeading
,
gast
.
KindListItem
:
lastNodeWasBlock
=
true
default
:
// Not a block element
}
return
gast
.
WalkContinue
,
nil
}
...
...
@@ -321,14 +323,14 @@ func (s *service) GenerateSnippet(content []byte, maxLength int) (string, error)
return
strings
.
TrimSpace
(
snippet
),
nil
}
// ValidateContent checks if the markdown content is valid
// ValidateContent checks if the markdown content is valid
.
func
(
s
*
service
)
ValidateContent
(
content
[]
byte
)
error
{
// Try to parse the content
_
,
err
:=
s
.
parse
(
content
)
return
err
}
// ExtractAll extracts tags, properties, and references in a single parse for efficiency
// ExtractAll extracts tags, properties, and references in a single parse for efficiency
.
func
(
s
*
service
)
ExtractAll
(
content
[]
byte
)
(
*
ExtractedData
,
error
)
{
root
,
err
:=
s
.
parse
(
content
)
if
err
!=
nil
{
...
...
@@ -362,15 +364,9 @@ func (s *service) ExtractAll(content []byte) (*ExtractedData, error) {
case
gast
.
KindLink
,
mast
.
KindWikilink
:
data
.
Property
.
HasLink
=
true
case
mast
.
KindWikilink
:
data
.
Property
.
HasLink
=
true
case
gast
.
KindCodeBlock
,
gast
.
KindFencedCodeBlock
,
gast
.
KindCodeSpan
:
data
.
Property
.
HasCode
=
true
case
gast
.
KindCodeSpan
:
data
.
Property
.
HasCode
=
true
case
east
.
KindTaskCheckBox
:
data
.
Property
.
HasTaskList
=
true
if
checkBox
,
ok
:=
n
.
(
*
east
.
TaskCheckBox
);
ok
{
...
...
@@ -378,6 +374,8 @@ func (s *service) ExtractAll(content []byte) (*ExtractedData, error) {
data
.
Property
.
HasIncompleteTasks
=
true
}
}
default
:
// No special handling for other node types
}
return
gast
.
WalkContinue
,
nil
...
...
@@ -393,7 +391,7 @@ func (s *service) ExtractAll(content []byte) (*ExtractedData, error) {
return
data
,
nil
}
// RenameTag renames all occurrences of oldTag to newTag in content
// RenameTag renames all occurrences of oldTag to newTag in content
.
func
(
s
*
service
)
RenameTag
(
content
[]
byte
,
oldTag
,
newTag
string
)
(
string
,
error
)
{
root
,
err
:=
s
.
parse
(
content
)
if
err
!=
nil
{
...
...
@@ -425,7 +423,7 @@ func (s *service) RenameTag(content []byte, oldTag, newTag string) (string, erro
return
mdRenderer
.
Render
(
root
,
content
),
nil
}
// uniqueLowercase returns unique lowercase strings from input
// uniqueLowercase returns unique lowercase strings from input
.
func
uniqueLowercase
(
strs
[]
string
)
[]
string
{
seen
:=
make
(
map
[
string
]
bool
)
var
result
[]
string
...
...
@@ -441,7 +439,7 @@ func uniqueLowercase(strs []string) []string {
return
result
}
// truncateAtWord truncates a string at the last word boundary before maxLength
// truncateAtWord truncates a string at the last word boundary before maxLength
.
func
truncateAtWord
(
s
string
,
maxLength
int
)
string
{
if
len
(
s
)
<=
maxLength
{
return
s
...
...
plugin/markdown/markdown_test.go
View file @
6cb96ef6
...
...
@@ -446,7 +446,7 @@ func TestTruncateAtWord(t *testing.T) {
}
}
// Benchmark tests
// Benchmark tests
.
func
BenchmarkGenerateSnippet
(
b
*
testing
.
B
)
{
svc
:=
NewService
()
content
:=
[]
byte
(
`# Large Document
...
...
plugin/markdown/parser/tag.go
View file @
6cb96ef6
...
...
@@ -10,7 +10,7 @@ import (
type
tagParser
struct
{}
// NewTagParser creates a new inline parser for #tag syntax
// NewTagParser creates a new inline parser for #tag syntax
.
func
NewTagParser
()
parser
.
InlineParser
{
return
&
tagParser
{}
}
...
...
@@ -20,8 +20,8 @@ func (*tagParser) Trigger() []byte {
return
[]
byte
{
'#'
}
}
// Parse parses #tag syntax
func
(
p
*
tagParser
)
Parse
(
parent
gast
.
Node
,
block
text
.
Reader
,
pc
parser
.
Context
)
gast
.
Node
{
// Parse parses #tag syntax
.
func
(
*
tagParser
)
Parse
(
_
gast
.
Node
,
block
text
.
Reader
,
_
parser
.
Context
)
gast
.
Node
{
line
,
_
:=
block
.
PeekLine
()
// Must start with #
...
...
plugin/markdown/parser/tag_test.go
View file @
6cb96ef6
...
...
@@ -110,7 +110,8 @@ func TestTagParser(t *testing.T) {
require
.
NotNil
(
t
,
node
,
"Expected tag to be parsed"
)
require
.
IsType
(
t
,
&
mast
.
TagNode
{},
node
)
tagNode
:=
node
.
(
*
mast
.
TagNode
)
tagNode
,
ok
:=
node
.
(
*
mast
.
TagNode
)
require
.
True
(
t
,
ok
,
"Expected node to be *mast.TagNode"
)
assert
.
Equal
(
t
,
tt
.
expectedTag
,
string
(
tagNode
.
Tag
))
}
else
{
assert
.
Nil
(
t
,
node
,
"Expected tag NOT to be parsed"
)
...
...
@@ -137,7 +138,8 @@ func TestTagParser_MultipleTags(t *testing.T) {
// Parse first tag
node1
:=
p
.
Parse
(
nil
,
reader
,
ctx
)
require
.
NotNil
(
t
,
node1
)
tagNode1
:=
node1
.
(
*
mast
.
TagNode
)
tagNode1
,
ok
:=
node1
.
(
*
mast
.
TagNode
)
require
.
True
(
t
,
ok
,
"Expected node1 to be *mast.TagNode"
)
assert
.
Equal
(
t
,
"tag1"
,
string
(
tagNode1
.
Tag
))
// Advance past the space
...
...
@@ -146,7 +148,8 @@ func TestTagParser_MultipleTags(t *testing.T) {
// Parse second tag
node2
:=
p
.
Parse
(
nil
,
reader
,
ctx
)
require
.
NotNil
(
t
,
node2
)
tagNode2
:=
node2
.
(
*
mast
.
TagNode
)
tagNode2
,
ok
:=
node2
.
(
*
mast
.
TagNode
)
require
.
True
(
t
,
ok
,
"Expected node2 to be *mast.TagNode"
)
assert
.
Equal
(
t
,
"tag2"
,
string
(
tagNode2
.
Tag
))
}
...
...
plugin/markdown/parser/wikilink.go
View file @
6cb96ef6
...
...
@@ -12,7 +12,7 @@ import (
type
wikilinkParser
struct
{}
// NewWikilinkParser creates a new inline parser for [[...]] wikilink syntax
// NewWikilinkParser creates a new inline parser for [[...]] wikilink syntax
.
func
NewWikilinkParser
()
parser
.
InlineParser
{
return
&
wikilinkParser
{}
}
...
...
@@ -23,7 +23,7 @@ func (*wikilinkParser) Trigger() []byte {
}
// Parse parses [[target]] or [[target?params]] wikilink syntax.
func
(
*
wikilinkParser
)
Parse
(
parent
gast
.
Node
,
block
text
.
Reader
,
pc
parser
.
Context
)
gast
.
Node
{
func
(
*
wikilinkParser
)
Parse
(
_
gast
.
Node
,
block
text
.
Reader
,
_
parser
.
Context
)
gast
.
Node
{
line
,
_
:=
block
.
PeekLine
()
// Must start with [[
...
...
@@ -64,8 +64,8 @@ func (*wikilinkParser) Parse(parent gast.Node, block text.Reader, pc parser.Cont
return
node
}
// findClosingBrackets finds the position of ]] in the byte slice
// Returns -1 if not found
// findClosingBrackets finds the position of ]] in the byte slice
.
// Returns -1 if not found
.
func
findClosingBrackets
(
data
[]
byte
)
int
{
for
i
:=
0
;
i
<
len
(
data
)
-
1
;
i
++
{
if
data
[
i
]
==
']'
&&
data
[
i
+
1
]
==
']'
{
...
...
@@ -75,7 +75,7 @@ func findClosingBrackets(data []byte) int {
return
-
1
}
// parseTargetAndParams splits content on ? to extract target and parameters
// parseTargetAndParams splits content on ? to extract target and parameters
.
func
parseTargetAndParams
(
content
[]
byte
)
(
target
[]
byte
,
params
[]
byte
)
{
// Find ? separator
idx
:=
bytes
.
IndexByte
(
content
,
'?'
)
...
...
plugin/markdown/parser/wikilink_test.go
View file @
6cb96ef6
...
...
@@ -124,7 +124,8 @@ func TestWikilinkParser(t *testing.T) {
require
.
NotNil
(
t
,
node
,
"Expected wikilink to be parsed"
)
require
.
IsType
(
t
,
&
mast
.
WikilinkNode
{},
node
)
wikilinkNode
:=
node
.
(
*
mast
.
WikilinkNode
)
wikilinkNode
,
ok
:=
node
.
(
*
mast
.
WikilinkNode
)
require
.
True
(
t
,
ok
,
"Expected node to be *mast.WikilinkNode"
)
assert
.
Equal
(
t
,
tt
.
expectedTarget
,
string
(
wikilinkNode
.
Target
))
assert
.
Equal
(
t
,
tt
.
expectedParams
,
string
(
wikilinkNode
.
Params
))
}
else
{
...
...
plugin/markdown/renderer/markdown_renderer.go
View file @
6cb96ef6
...
...
@@ -252,7 +252,7 @@ func (r *MarkdownRenderer) renderListItem(node *gast.ListItem, source []byte, de
// Add list marker
if
list
.
IsOrdered
()
{
r
.
buf
.
WriteString
(
fmt
.
Sprintf
(
"%d. "
,
list
.
Start
)
)
fmt
.
Fprintf
(
r
.
buf
,
"%d. "
,
list
.
Start
)
list
.
Start
++
// Increment for next item
}
else
{
r
.
buf
.
WriteString
(
"- "
)
...
...
server/router/api/v1/memo_service.go
View file @
6cb96ef6
...
...
@@ -6,7 +6,6 @@ import (
"log/slog"
"strings"
"time"
"unicode/utf8"
"github.com/lithammer/shortuuid/v4"
"github.com/pkg/errors"
...
...
@@ -845,25 +844,6 @@ func (s *APIV1Service) getMemoContentSnippet(content string) (string, error) {
return
snippet
,
nil
}
func
substring
(
s
string
,
length
int
)
string
{
if
length
<=
0
{
return
""
}
runeCount
:=
0
byteIndex
:=
0
for
byteIndex
<
len
(
s
)
{
_
,
size
:=
utf8
.
DecodeRuneInString
(
s
[
byteIndex
:
])
byteIndex
+=
size
runeCount
++
if
runeCount
==
length
{
break
}
}
return
s
[
:
byteIndex
]
}
// parseMemoOrderBy parses the order_by field and sets the appropriate ordering in memoFind.
// Follows AIP-132: supports comma-separated list of fields with optional "desc" suffix.
// Example: "pinned desc, display_time desc" or "create_time asc".
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment