Skip to content

Commit e645348

Browse files
committed
linkify: strip leading/trailing parens and trailing dots
1 parent 23ba4f1 commit e645348

2 files changed

Lines changed: 41 additions & 12 deletions

File tree

internal/bull/render_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ full insecure URL: http://localhost:3333/
3838
3939
naked URL: go.dev/cl/1234
4040
41+
URL in parens: (https://example.com/in-parens)
42+
43+
URL with trailing period: https://example.com/trailing.
44+
4145
-- maybe consider?.md --
4246
something profound
4347
`))
@@ -125,6 +129,14 @@ something profound
125129
t.Errorf("GET /: response does not link to %q", want)
126130
}
127131

132+
if want := "https://example.com/in-parens"; !targets[want] {
133+
t.Errorf("GET /: response does not link to %q", want)
134+
}
135+
136+
if want := "https://example.com/trailing"; !targets[want] {
137+
t.Errorf("GET /: response does not link to %q", want)
138+
}
139+
128140
if want := "/best/stuff"; !targets[want] {
129141
t.Errorf("GET /: response does not link to %q", want)
130142
}

internal/linkify/linkify.go

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ var Kind = ast.NewNodeKind("linkify")
1919
type Node struct {
2020
ast.BaseInline
2121

22-
Link []byte
23-
withSchema bool
24-
leadingWhitespace bool
22+
Link []byte
23+
withSchema bool
24+
leading []byte
2525
}
2626

2727
func (*Node) Kind() ast.NodeKind { return Kind }
@@ -88,6 +88,7 @@ func (p *Parser) Parse(_ ast.Node, block text.Reader, _ parser.Context) ast.Node
8888
for skip < len(line) && util.IsSpace(line[skip]) {
8989
skip++
9090
}
91+
leading := line[:skip]
9192
line = line[skip:]
9293

9394
// find the next whitespace character or end of line
@@ -97,21 +98,37 @@ func (p *Parser) Parse(_ ast.Node, block text.Reader, _ parser.Context) ast.Node
9798
} else {
9899
stop += len(line)
99100
}
100-
word := line[:stop]
101+
102+
trimLeft := 0
103+
for trimLeft < stop && line[trimLeft] == '(' {
104+
trimLeft++
105+
}
106+
leading = append(leading, line[:trimLeft]...)
107+
trimRight := 0
108+
for trimRight < stop-trimLeft {
109+
switch line[stop-1-trimRight] {
110+
case ')', '.', ':', ',':
111+
trimRight++
112+
continue
113+
}
114+
break
115+
}
116+
117+
word := line[trimLeft : stop-trimRight]
101118
url, withSchema := isURL(word)
102119
if !url {
103120
return nil
104121
}
105122

106-
seg = seg.WithStart(seg.Start + skip)
107-
seg = seg.WithStop(seg.Start + stop)
123+
seg = seg.WithStart(seg.Start + skip + trimLeft)
124+
seg = seg.WithStop(seg.Start + stop - trimLeft - trimRight)
108125
n := Node{
109-
Link: block.Value(seg),
110-
withSchema: withSchema,
111-
leadingWhitespace: skip > 0,
126+
Link: block.Value(seg),
127+
withSchema: withSchema,
128+
leading: leading,
112129
}
113130
n.AppendChild(&n, ast.NewTextSegment(seg))
114-
block.Advance(skip + seg.Len())
131+
block.Advance(skip + trimLeft + seg.Len())
115132
return &n
116133
}
117134

@@ -130,8 +147,8 @@ func (r *Renderer) renderLinkify(w util.BufWriter, _ []byte, node ast.Node, ente
130147
}
131148

132149
if entering {
133-
if n.leadingWhitespace {
134-
w.WriteString(" ")
150+
if len(n.leading) > 0 {
151+
w.Write(n.leading)
135152
}
136153
if !n.withSchema {
137154
w.WriteString(`<a href="http://` + string(n.Link) + `">`)

0 commit comments

Comments
 (0)