1// Package autolink provides a function to detect and format GitHub links into
2// a more readable manner.
3package autolink
4
5import (
6 "fmt"
7 "regexp"
8)
9
10type pattern struct {
11 pattern *regexp.Regexp
12 yield func(m []string) string
13}
14
15var patterns = []pattern{
16 {
17 regexp.MustCompile(`^https?://github\.com/([A-z0-9_-]+)/([A-z0-9_-]+)/(issues?|pulls?|discussions?)/([0-9]+)$`),
18 func(m []string) string { return fmt.Sprintf("%s/%s#%s", m[1], m[2], m[4]) },
19 },
20 {
21 regexp.MustCompile(`^https?://github\.com/([A-z0-9_-]+)/([A-z0-9_-]+)/(issues?|pulls?|discussions?)/([0-9]+)#issuecomment-[0-9]+$`),
22 func(m []string) string { return fmt.Sprintf("%s/%s#%s (comment)", m[1], m[2], m[4]) },
23 },
24 {
25 regexp.MustCompile(`^https?://github\.com/([A-z0-9_-]+)/([A-z0-9_-]+)/pulls?/([0-9]+)#discussion_r[0-9]+$`),
26 func(m []string) string { return fmt.Sprintf("%s/%s#%s (comment)", m[1], m[2], m[3]) },
27 },
28 {
29 regexp.MustCompile(`^https?://github\.com/([A-z0-9_-]+)/([A-z0-9_-]+)/pulls?/([0-9]+)#pullrequestreview-[0-9]+$`),
30 func(m []string) string { return fmt.Sprintf("%s/%s#%s (review)", m[1], m[2], m[3]) },
31 },
32 {
33 regexp.MustCompile(`^https?://github\.com/([A-z0-9_-]+)/([A-z0-9_-]+)/discussions/([0-9]+)#discussioncomment-[0-9]+$`),
34 func(m []string) string { return fmt.Sprintf("%s/%s#%s (comment)", m[1], m[2], m[3]) },
35 },
36 {
37 regexp.MustCompile(`^https?://github\.com/([A-z0-9_-]+)/([A-z0-9_-]+)/commit/([A-z0-9]{7,})(#.*)?$`),
38 func(m []string) string { return fmt.Sprintf("%s/%s@%s", m[1], m[2], m[3][:7]) },
39 },
40 {
41 regexp.MustCompile(`^https?://github\.com/([A-z0-9_-]+)/([A-z0-9_-]+)/pulls?/[0-9]+/commits/([A-z0-9]{7,})(#.*)?$`),
42 func(m []string) string { return fmt.Sprintf("%s/%s@%s", m[1], m[2], m[3][:7]) },
43 },
44}
45
46// Detect checks if the given URL matches any of the known patterns and
47// returns a human-readable formatted string if a match is found.
48func Detect(u string) (string, bool) {
49 for _, p := range patterns {
50 if m := p.pattern.FindStringSubmatch(u); len(m) > 0 {
51 return p.yield(m), true
52 }
53 }
54 return "", false
55}