aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTassilo Horn2020-06-11 17:02:02 +0200
committerTassilo Horn2020-06-17 22:03:50 +0200
commit5502eedf90d0da27df0c6c1fa33389d849d59a80 (patch)
treef92d3435ebe288ce9632070915d55ce5daa1018b
parent21b03faed44913ba0b0e3d54d2ffdb0ac067fae8 (diff)
downloademacs-5502eedf90d0da27df0c6c1fa33389d849d59a80.tar.gz
emacs-5502eedf90d0da27df0c6c1fa33389d849d59a80.zip
Auto-setup for bug-reference-mode
Tries to guess suitable bug-reference-bug-regexp and bug-reference-url-format values based on version control URL (in file buffers) and mail information (in Gnus summary and article buffers). * lisp/progmodes/bug-reference.el (bug-reference--maybe-setup-from-vc): New defun. (bug-reference-setup-from-vc-alist): New defvar defining setup rules based on version control URL. (bug-reference-try-setup-from-vc): New defun using above defvar. (bug-reference--maybe-setup-from-mail): New defun. (bug-reference-setup-from-mail-alist): New defvar defining setup rules based on mail/newsgroups and header values. (bug-reference-try-setup-from-gnus): New defun using above defvar. (bug-reference--try-setup-gnus-article): New defun. (bug-reference--run-auto-setup): New defun. (bug-reference-mode): Call bug-reference--run-auto-setup as :after-hook. (bug-reference-prog-mode): Call bug-reference--run-auto-setup as :after-hook.
-rw-r--r--lisp/progmodes/bug-reference.el218
-rw-r--r--lisp/vc/vc.el10
2 files changed, 225 insertions, 3 deletions
diff --git a/lisp/progmodes/bug-reference.el b/lisp/progmodes/bug-reference.el
index 02af263ec34..50bd3661eff 100644
--- a/lisp/progmodes/bug-reference.el
+++ b/lisp/progmodes/bug-reference.el
@@ -139,12 +139,229 @@ The second subexpression should match the bug reference (usually a number)."
139 (when url 139 (when url
140 (browse-url url)))))) 140 (browse-url url))))))
141 141
142(defun bug-reference--maybe-setup-from-vc (url url-rx bug-rx bug-url-fmt)
143 (when (string-match url-rx url)
144 (setq-local bug-reference-bug-regexp bug-rx)
145 (setq-local bug-reference-url-format
146 (let (groups)
147 (dotimes (i (/ (length (match-data)) 2))
148 (push (match-string i url) groups))
149 (funcall bug-url-fmt (nreverse groups))))))
150
151(defvar bug-reference-setup-from-vc-alist
152 `(;;
153 ;; GNU projects on savannah.
154 ;;
155 ;; Not all of them use debbugs but that doesn't really matter
156 ;; because the auto-setup is only performed if
157 ;; `bug-reference-url-format' and `bug-reference-bug-regexp'
158 ;; aren't set already.
159 ("git\\.\\(?:sv\\|savannah\\)\\.gnu\\.org:"
160 "\\<\\([Bb]ug ?#?\\)\\([0-9]+\\(?:#[0-9]+\\)?\\)\\>"
161 ,(lambda (_) "https://debbugs.gnu.org/%s"))
162 ;;
163 ;; GitHub projects.
164 ;;
165 ;; Here #17 may refer to either an issue or a pull request but
166 ;; visiting the issue/17 web page will automatically redirect to
167 ;; the pull/17 page if 17 is a PR. Explicit user/project#17 links
168 ;; to possibly different projects are also supported.
169 ("[/@]github.com[/:]\\([.A-Za-z0-9_/-]+\\)\\.git"
170 "\\([.A-Za-z0-9_/-]+\\)?\\(?:#\\)\\([0-9]+\\)\\>"
171 ,(lambda (groups)
172 (let ((ns-project (nth 1 groups)))
173 (lambda ()
174 (concat "https://github.com/"
175 (or
176 ;; Explicit user/proj#18 link.
177 (match-string 1)
178 ns-project)
179 "/issues/"
180 (match-string 2))))))
181 ;;
182 ;; GitLab projects.
183 ;;
184 ;; Here #18 is an issue and !17 is a merge request. Explicit
185 ;; namespace/project#18 or namespace/project!17 references to
186 ;; possibly different projects are also supported.
187 ("[/@]gitlab.com[/:]\\([.A-Za-z0-9_/-]+\\)\\.git"
188 "\\(?1:[.A-Za-z0-9_/-]+\\)?\\(?3:[#!]\\)\\(?2:[0-9]+\\)\\>"
189 ,(lambda (groups)
190 (let ((ns-project (nth 1 groups)))
191 (lambda ()
192 (concat "https://gitlab.com/"
193 (or (match-string 1)
194 ns-project)
195 "/-/"
196 (if (string= (match-string 3) "#")
197 "issues/"
198 "merge_requests/")
199 (match-string 2)))))))
200 "An alist for setting up `bug-reference-mode' based on VC URL.
201
202Each element has the form (URL-REGEXP BUG-REGEXP URL-FORMAT-FN).
203
204URL-REGEXP is matched against the version control URL of the
205current buffer's file. If it matches, BUG-REGEXP is set as
206`bug-reference-bug-regexp'. URL-FORMAT-FN is a function of one
207argument that receives a list of the groups 0 to N of matching
208URL-REGEXP against the VCS URL and returns the value to be set as
209`bug-reference-url-format'.")
210
211(defun bug-reference-try-setup-from-vc ()
212 "Try setting up `bug-reference-mode' based on VC information.
213Test each configuration in `bug-reference-setup-from-vc-alist'
214and apply it if applicable."
215 (when buffer-file-name
216 (let* ((backend (vc-responsible-backend buffer-file-name t))
217 (url
218 (or (ignore-errors
219 (vc-call-backend backend 'repository-url "upstream"))
220 (ignore-errors
221 (vc-call-backend backend 'repository-url)))))
222 (when url
223 (catch 'found
224 (dolist (config bug-reference-setup-from-vc-alist)
225 (when (apply #'bug-reference--maybe-setup-from-vc
226 url config)
227 (throw 'found t))))))))
228
229(defvar bug-reference-setup-from-mail-alist
230 `((,(regexp-opt '("emacs" "auctex" "gnus") 'words)
231 ,(regexp-opt '("@debbugs.gnu.org" "-devel@gnu.org"
232 ;; List-Id of Gnus devel mailing list.
233 "ding.gnus.org"))
234 "\\([Bb]ug ?#?\\)\\([0-9]+\\(?:#[0-9]+\\)?\\)"
235 "https://debbugs.gnu.org/%s"))
236 "An alist for setting up `bug-reference-mode' in mail modes.
237
238This takes action if `bug-reference-mode' is enabled in group and
239message buffers of Emacs mail clients. Currently, only Gnus is
240supported.
241
242Each element has the form
243
244 (GROUP-REGEXP HEADER-REGEXP BUG-REGEXP URL-FORMAT)
245
246GROUP-REGEXP is a regexp matched against the current mail folder
247or newsgroup name. HEADER-REGEXP is a regexp matched against the
248From, To, Cc, Newsgroup, and List-ID header values of the current
249mail or newsgroup message. If any of those matches, BUG-REGEXP
250is set as `bug-reference-bug-regexp' and URL-FORMAT is set as
251`bug-reference-url-format'.
252
253Note: In Gnus, if a summary buffer has been set up based on
254GROUP-REGEXP, all article buffers opened from there will get the
255same `bug-reference-url-format' and `bug-reference-url-format'.")
256
257(defvar gnus-newsgroup-name)
258
259(defun bug-reference--maybe-setup-from-mail (group header-values)
260 "Set up according to mail GROUP or HEADER-VALUES.
261Group is a mail group/folder name and HEADER-VALUES is a list of
262mail header values, e.g., the values of From, To, Cc, List-ID,
263and Newsgroup.
264
265If any GROUP-REGEXP or HEADER-REGEXP of
266`bug-reference-setup-from-mail-alist' matches GROUP or any
267element in HEADER-VALUES, the corresponding BUG-REGEXP and
268URL-FORMAT are set."
269 (catch 'setup-done
270 (dolist (config bug-reference-setup-from-mail-alist)
271 (when (or
272 (and group
273 (car config)
274 (string-match-p (car config) group))
275 (and header-values
276 (nth 1 config)
277 (catch 'matching-header
278 (dolist (h header-values)
279 (when (and h (string-match-p (nth 1 config) h))
280 (throw 'matching-header t))))))
281 (setq-local bug-reference-bug-regexp (nth 2 config))
282 (setq-local bug-reference-url-format (nth 3 config))
283 (throw 'setup-done t)))))
284
285(defun bug-reference-try-setup-from-gnus ()
286 "Try setting up `bug-reference-mode' based on Gnus group or article.
287Test each configuration in `bug-reference-setup-from-mail-alist'
288and set it if applicable."
289 (when (and (derived-mode-p 'gnus-summary-mode)
290 (bound-and-true-p gnus-newsgroup-name))
291 ;; Gnus reuses its article buffer so we have to check whenever the
292 ;; article changes.
293 (add-hook 'gnus-article-prepare-hook
294 #'bug-reference--try-setup-gnus-article)
295 (bug-reference--maybe-setup-from-mail gnus-newsgroup-name nil)))
296
297(defvar gnus-article-buffer)
298(defvar gnus-original-article-buffer)
299(defvar gnus-summary-buffer)
300
301(defun bug-reference--try-setup-gnus-article ()
302 (with-demoted-errors
303 "Error in bug-reference--try-setup-gnus-article: %S"
304 (when (and bug-reference-mode ;; Only if enabled in article buffers.
305 (derived-mode-p
306 'gnus-article-mode
307 ;; Apparently, gnus-article-prepare-hook is run in the
308 ;; summary buffer...
309 'gnus-summary-mode)
310 gnus-article-buffer
311 gnus-original-article-buffer
312 (buffer-live-p (get-buffer gnus-article-buffer))
313 (buffer-live-p (get-buffer gnus-original-article-buffer)))
314 (with-current-buffer gnus-article-buffer
315 (catch 'setup-done
316 ;; Copy over the values from the summary buffer.
317 (when (and gnus-summary-buffer
318 (buffer-live-p gnus-summary-buffer))
319 (setq-local bug-reference-bug-regexp
320 (with-current-buffer gnus-summary-buffer
321 bug-reference-bug-regexp))
322 (setq-local bug-reference-url-format
323 (with-current-buffer gnus-summary-buffer
324 bug-reference-url-format))
325 (when (and bug-reference-bug-regexp
326 bug-reference-url-format)
327 (throw 'setup-done t)))
328 ;; If the summary had no values, try setting according to
329 ;; the values of the From, To, and Cc headers.
330 (let (header-values)
331 (with-current-buffer
332 (get-buffer gnus-original-article-buffer)
333 (save-excursion
334 (goto-char (point-min))
335 ;; The Newsgroup is omitted because we already matched
336 ;; based on group name in the summary buffer.
337 (dolist (field '("list-id" "to" "from" "cc"))
338 (let ((val (mail-fetch-field field)))
339 (when val
340 (push val header-values))))))
341 (bug-reference--maybe-setup-from-mail nil header-values)))))))
342
343(defun bug-reference--run-auto-setup ()
344 (when (or bug-reference-mode
345 bug-reference-prog-mode)
346 ;; Automatic setup only if the variables aren't already set, e.g.,
347 ;; by a local variables section in the file.
348 (unless (and bug-reference-bug-regexp
349 bug-reference-url-format)
350 (with-demoted-errors
351 "Error during bug-reference auto-setup: %S"
352 (catch 'setup
353 (dolist (f (list #'bug-reference-try-setup-from-vc
354 #'bug-reference-try-setup-from-gnus))
355 (when (funcall f)
356 (throw 'setup t))))))))
357
142;;;###autoload 358;;;###autoload
143(define-minor-mode bug-reference-mode 359(define-minor-mode bug-reference-mode
144 "Toggle hyperlinking bug references in the buffer (Bug Reference mode)." 360 "Toggle hyperlinking bug references in the buffer (Bug Reference mode)."
145 nil 361 nil
146 "" 362 ""
147 nil 363 nil
364 :after-hook (bug-reference--run-auto-setup)
148 (if bug-reference-mode 365 (if bug-reference-mode
149 (jit-lock-register #'bug-reference-fontify) 366 (jit-lock-register #'bug-reference-fontify)
150 (jit-lock-unregister #'bug-reference-fontify) 367 (jit-lock-unregister #'bug-reference-fontify)
@@ -158,6 +375,7 @@ The second subexpression should match the bug reference (usually a number)."
158 nil 375 nil
159 "" 376 ""
160 nil 377 nil
378 :after-hook (bug-reference--run-auto-setup)
161 (if bug-reference-prog-mode 379 (if bug-reference-prog-mode
162 (jit-lock-register #'bug-reference-fontify) 380 (jit-lock-register #'bug-reference-fontify)
163 (jit-lock-unregister #'bug-reference-fontify) 381 (jit-lock-unregister #'bug-reference-fontify)
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index ce947d21f95..9b12d449785 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -964,7 +964,7 @@ use."
964 (throw 'found bk)))) 964 (throw 'found bk))))
965 965
966;;;###autoload 966;;;###autoload
967(defun vc-responsible-backend (file) 967(defun vc-responsible-backend (file &optional no-error)
968 "Return the name of a backend system that is responsible for FILE. 968 "Return the name of a backend system that is responsible for FILE.
969 969
970If FILE is already registered, return the 970If FILE is already registered, return the
@@ -974,7 +974,10 @@ responsible for FILE is returned.
974 974
975Note that if FILE is a symbolic link, it will not be resolved -- 975Note that if FILE is a symbolic link, it will not be resolved --
976the responsible backend system for the symbolic link itself will 976the responsible backend system for the symbolic link itself will
977be reported." 977be reported.
978
979If NO-ERROR is nil, signal an error that no VC backend is
980responsible for the given file."
978 (or (and (not (file-directory-p file)) (vc-backend file)) 981 (or (and (not (file-directory-p file)) (vc-backend file))
979 (catch 'found 982 (catch 'found
980 ;; First try: find a responsible backend. If this is for registration, 983 ;; First try: find a responsible backend. If this is for registration,
@@ -982,7 +985,8 @@ be reported."
982 (dolist (backend vc-handled-backends) 985 (dolist (backend vc-handled-backends)
983 (and (vc-call-backend backend 'responsible-p file) 986 (and (vc-call-backend backend 'responsible-p file)
984 (throw 'found backend)))) 987 (throw 'found backend))))
985 (error "No VC backend is responsible for %s" file))) 988 (unless no-error
989 (error "No VC backend is responsible for %s" file))))
986 990
987(defun vc-expand-dirs (file-or-dir-list backend) 991(defun vc-expand-dirs (file-or-dir-list backend)
988 "Expands directories in a file list specification. 992 "Expands directories in a file list specification.