autolink.go

 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}