-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
linkifier.el
151 lines (136 loc) · 5.61 KB
/
linkifier.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
;;; linkifier.el --- Convert text into clickable links, via regexp
;;
;; Copyright (C) 2023 Steve Kemp
;;
;; Version: 0.3
;; Keywords: links, web, utility, regexp
;; Author: Steve Kemp <[email protected]>
;;
;; This file is not (YET) part of GNU Emacs.
;; This is free software; you can redistribute it and/or modify it under
;; the terms of the GNU General Public License as published by the Free
;; Software Foundation; either version 2, or (at your option) any later
;; version.
;;
;; This is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
;; for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
;; MA 02111-1307, USA.
;; Setup the regexp to destination mappings. These consist of pairs of
;; entries - the first entry is a regular expression, the second is the
;; destination for the link.
;;
;; For example we have "XXX-1234" going to Jira, and BUG-1234 going
;; to bugzilla with the following configuration:
;;
;; (setq linkifier-patterns '(
;; ("\\\<XXX-[0-9]+\\\>" "https://jira.example.com/browse/%s")
;; ("\\\<BUG-[0-9]+\\\>" "https://bugzilla.example.com/show?id=%s")))
;;
;;
;; Note that "\<XXX\>" means that we only match XXX when
;; not surrounded by "word constituent" characters.
;;
;;
;; Capture Groups:
;;
;; By default we expand "%s" within the destination as the literal
;; text of the button, which works for things like jira/bugzilla,
;; however it is possible to use a capture-group to ensure that
;; we only expand part of the match.
;;
;; For example consider the following two config-items for looking
;; at hacker-news stories, or youtube videos:
;;
;; ("\\\<HN-\\([0-9]+\\)\\\>" "https://news.ycombinator.com/item?id=%s")
;; ("\\\<YOUTUBE-\\([0-9a-zA-Z]+\\)\\\>" "https://www.youtube.com/watch?v=%s")
;;
;; Here we're using a capture-group \\(...\\) to ensure that we only
;; capture the parameter, which is then added to the link. Without
;; this we'd end up sending the user to destinations such as:
;;
;; https://news.ycombinator.com/item?id=HN-34907651
;; https://www.youtube.com/watch?v=YOUTUBE-RyTlSAkoGJg
;;
;; The "HN-" and "YOUTUBE-" prefixes would obviously cause the resulting
;; pages to return 404.
;;
;;
;; VERSION HISTORY
;;
;; 0.1 - Hacked up initial implementation.
;; 0.2 - Added support for capture groups.
;; 0.3 - Fixed warnings
;; .emacs.d/ui/linkifier.el: Warning: ‘loop’ is an obsolete alias (as of 27.1); use ‘cl-loop’ instead.
;; .emacs.d/ui/linkifier.el: Warning: Use keywords rather than deprecated positional arguments to `define-minor-mode'
;;
;;
;;
;; UPDATE
;;
;;
;; Alternatives - This whole package could be replaced by `bug-reference-mode':
;;
;; ; How to find the bug reference in text
;; (setq bug-reference-bug-regexp "\\(Trac-\\([0-9]+\\)\\)")
;;
;; ; How to build bug-tracker URL
;; (setq bug-reference-url-format "https://trac.edgewall.org/ticket/%s")
;;
;; ; Enable bug-reference parsing in prog buffers
;; (add-hook 'prog-mode-hook #'bug-reference-prog-mode)
;;
;;
;;
;; The default is nil as the destinations and patterns are entirely
;; site & user-specific.
(defvar linkifier-patterns nil)
;; Ensure we have a type for our buttons.
(define-button-type 'linkifier)
(defun linkifier-insert-buttons (beg end)
"Insert buttons within the current buffer, between the specified start and end.
What this does is iterate over the regexp&URL pairs in `linkifier-patterns` and
replace any text that matches the regular expression specified with a button, which
will open the destination URL when selected."
(remove-overlays beg end 'type 'linkifier)
(cl-loop for (regexp . destination) in linkifier-patterns do
(save-excursion
(goto-char beg)
(while (re-search-forward regexp end t)
;; default to using the whole match (i.e. "XXXX-1234")
;; but if there is a capture group, then use the value of the
;; first captured variable.
(let ((match (match-string 0)))
(if (match-string 1)
(setq match (match-string 1)))
(make-button (match-beginning 0)
(match-end 0)
'dest destination ; from the config
'match match ; either literal button-text or first capture value
'type 'linkifier
'face 'link ; make this button stand out.
'action `(lambda (button)
(browse-url (format (car (button-get button 'dest)) (button-get button 'match))))
'follow-link t
'text (match-string 0)))))))
(define-minor-mode linkifier-mode
"`linkifier-mode` is a simple minor mode for turning text matching a
series of regular expressions into buttons/hyperlinks with templated
destinations.
It can be handy for converting text such as FOO-123 into a hyperlink
to a Jira installation, bugzilla instance, or similar."
:init-value nil
:lighter " linkifier"
(cond
(linkifier-mode
(jit-lock-register #'linkifier-insert-buttons)
(linkifier-insert-buttons (point-min) (point-max)))
(t
(jit-lock-unregister #'linkifier-insert-buttons)
(remove-overlays (point-min) (point-max) 'type 'linkifier))))
(provide 'linkifier)