diff options
Diffstat (limited to 'lisp')
| -rw-r--r-- | lisp/progmodes/bug-reference.el | 197 | ||||
| -rw-r--r-- | lisp/vc/vc.el | 10 |
2 files changed, 204 insertions, 3 deletions
diff --git a/lisp/progmodes/bug-reference.el b/lisp/progmodes/bug-reference.el index 02af263ec34..20558de6b02 100644 --- a/lisp/progmodes/bug-reference.el +++ b/lisp/progmodes/bug-reference.el | |||
| @@ -139,12 +139,208 @@ 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 | `(;; GNU projects on savannah. FIXME: Only a fraction of | ||
| 153 | ;; them uses debbugs. | ||
| 154 | ("git\\.\\(?:sv\\|savannah\\)\\.gnu\\.org:" | ||
| 155 | "\\<\\([Bb]ug ?#?\\)\\([0-9]+\\(?:#[0-9]+\\)?\\)\\>" | ||
| 156 | ,(lambda (_) "https://debbugs.gnu.org/%s")) | ||
| 157 | ;; GitHub projects. Here #17 may refer to either an issue | ||
| 158 | ;; or a pull request but visiting the issue/17 web page | ||
| 159 | ;; will automatically redirect to the pull/17 page if 17 is | ||
| 160 | ;; a PR. Explicit user/project#17 links to possibly | ||
| 161 | ;; different projects are also supported. | ||
| 162 | ("[/@]github.com[/:]\\([.A-Za-z0-9_/-]+\\)\\.git" | ||
| 163 | "\\([.A-Za-z0-9_/-]+\\)?\\(?:#\\)\\([0-9]+\\)\\>" | ||
| 164 | ,(lambda (groups) | ||
| 165 | (let ((ns-project (nth 1 groups))) | ||
| 166 | (lambda () | ||
| 167 | (concat "https://github.com/" | ||
| 168 | (or | ||
| 169 | ;; Explicit user/proj#18 link. | ||
| 170 | (match-string 1) | ||
| 171 | ns-project) | ||
| 172 | "/issues/" | ||
| 173 | (match-string 2)))))) | ||
| 174 | ;; GitLab projects. Here #18 is an issue and !17 is a merge | ||
| 175 | ;; request. Explicit namespace/project#18 references to possibly | ||
| 176 | ;; different projects are also supported. | ||
| 177 | ("[/@]gitlab.com[/:]\\([.A-Za-z0-9_/-]+\\)\\.git" | ||
| 178 | "\\(?1:[.A-Za-z0-9_/-]+\\)?\\(?3:[#!]\\)\\(?2:[0-9]+\\)\\>" | ||
| 179 | ,(lambda (groups) | ||
| 180 | (let ((ns-project (nth 1 groups))) | ||
| 181 | (lambda () | ||
| 182 | (concat "https://gitlab.com/" | ||
| 183 | (or (match-string 1) | ||
| 184 | ns-project) | ||
| 185 | "/-/" | ||
| 186 | (if (string= (match-string 3) "#") | ||
| 187 | "issues/" | ||
| 188 | "merge_requests/") | ||
| 189 | (match-string 2))))))) | ||
| 190 | "An alist for setting up `bug-reference-mode' based on VC URL. | ||
| 191 | |||
| 192 | Each element has the form (URL-REGEXP BUG-REGEXP URL-FORMAT-FN). | ||
| 193 | |||
| 194 | URL-REGEXP is matched against the version control URL of the | ||
| 195 | current buffer's file. If it matches, BUG-REGEXP is set as | ||
| 196 | `bug-reference-bug-regexp'. URL-FORMAT-FN is a function of one | ||
| 197 | argument that receives a list of the groups 0 to N of matching | ||
| 198 | URL-REGEXP against the VCS URL and return the value to be set as | ||
| 199 | `bug-reference-url-format'.") | ||
| 200 | |||
| 201 | (defun bug-reference-try-setup-from-vc () | ||
| 202 | "Try setting up `bug-reference-mode' based on VCS information. | ||
| 203 | Tests each configuration from `bug-reference-setup-from-vc-alist' | ||
| 204 | and sets it if applicable." | ||
| 205 | (when buffer-file-name | ||
| 206 | (let* ((backend (vc-responsible-backend buffer-file-name t)) | ||
| 207 | (url | ||
| 208 | (or (ignore-errors | ||
| 209 | (vc-call-backend backend 'repository-url "upstream")) | ||
| 210 | (ignore-errors | ||
| 211 | (vc-call-backend backend 'repository-url))))) | ||
| 212 | (when url | ||
| 213 | (catch 'found | ||
| 214 | (dolist (config bug-reference-setup-from-vc-alist) | ||
| 215 | (when (apply #'bug-reference--maybe-setup-from-vc | ||
| 216 | url config) | ||
| 217 | (throw 'found t)))))))) | ||
| 218 | |||
| 219 | (defvar bug-reference-setup-from-mail-alist | ||
| 220 | `((,(regexp-opt '("emacs" "auctex" "gnus") 'words) | ||
| 221 | ,(regexp-opt '("@debbugs.gnu.org" "-devel@gnu.org" | ||
| 222 | ;; List-Id of Gnus devel mailing list. | ||
| 223 | "ding.gnus.org")) | ||
| 224 | "\\([Bb]ug ?#?\\)\\([0-9]+\\(?:#[0-9]+\\)?\\)" | ||
| 225 | "https://debbugs.gnu.org/%s")) | ||
| 226 | ;; TODO: Adapt docstring! | ||
| 227 | "An alist for setting up `bug-reference-mode' based in mail modes. | ||
| 228 | |||
| 229 | This takes action if `bug-reference-mode' is enabled in group and | ||
| 230 | message buffers of Emacs mail clients. Currently, only Gnus is | ||
| 231 | supported. | ||
| 232 | |||
| 233 | Each element has the form | ||
| 234 | |||
| 235 | (GROUP-REGEXP HEADER-REGEXP BUG-REGEXP URL-FORMAT) | ||
| 236 | |||
| 237 | GROUP-REGEXP is a regexp matched against the current mail folder | ||
| 238 | name or newsgroup. HEADER-REGEXP is a regexp matched against the | ||
| 239 | From, To, Cc, and List-ID header values of the current mail or | ||
| 240 | newsgroup message. If any of those matches, BUG-REGEXP is set as | ||
| 241 | `bug-reference-bug-regexp' and URL-FORMAT is set as | ||
| 242 | `bug-reference-url-format'. | ||
| 243 | |||
| 244 | Note: In Gnus, if a summary buffer has been set up based on | ||
| 245 | GROUP-REGEXP, all article buffers opened from there will get the | ||
| 246 | same `bug-reference-url-format' and `bug-reference-url-format'.") | ||
| 247 | |||
| 248 | (defvar gnus-newsgroup-name) | ||
| 249 | |||
| 250 | (defun bug-reference--maybe-setup-from-mail (group headers) | ||
| 251 | (catch 'setup-done | ||
| 252 | (dolist (config bug-reference-setup-from-mail-alist) | ||
| 253 | (when (or | ||
| 254 | (and group | ||
| 255 | (car config) | ||
| 256 | (string-match-p (car config) group)) | ||
| 257 | (and headers | ||
| 258 | (nth 1 config) | ||
| 259 | (catch 'matching-header | ||
| 260 | (dolist (h headers) | ||
| 261 | (when (and h (string-match-p (nth 1 config) h)) | ||
| 262 | (throw 'matching-header t)))))) | ||
| 263 | (setq-local bug-reference-bug-regexp (nth 2 config)) | ||
| 264 | (setq-local bug-reference-url-format (nth 3 config)) | ||
| 265 | (throw 'setup-done t))))) | ||
| 266 | |||
| 267 | (defun bug-reference-try-setup-from-gnus () | ||
| 268 | "Try setting up `bug-reference-mode' based on Gnus group or article. | ||
| 269 | Tests each configuration from `bug-reference-setup-from-mail-alist' | ||
| 270 | and sets it if applicable." | ||
| 271 | (when (and (derived-mode-p 'gnus-summary-mode) | ||
| 272 | (bound-and-true-p gnus-newsgroup-name)) | ||
| 273 | ;; Gnus reuses its article buffer so we have to check whenever the | ||
| 274 | ;; article changes. | ||
| 275 | (add-hook 'gnus-article-prepare-hook | ||
| 276 | #'bug-reference--try-setup-gnus-article) | ||
| 277 | (bug-reference--maybe-setup-from-mail gnus-newsgroup-name nil))) | ||
| 278 | |||
| 279 | (defvar gnus-article-buffer) | ||
| 280 | (defvar gnus-summary-buffer) | ||
| 281 | (declare-function mail-header-extract "mailheader") | ||
| 282 | (declare-function mail-header "mailheader") | ||
| 283 | |||
| 284 | (defun bug-reference--try-setup-gnus-article () | ||
| 285 | (with-demoted-errors | ||
| 286 | "Error in bug-reference--try-setup-gnus-article: %S" | ||
| 287 | (when (and bug-reference-mode ;; Only if enabled in article buffers. | ||
| 288 | (derived-mode-p | ||
| 289 | 'gnus-article-mode | ||
| 290 | ;; Apparently, gnus-article-prepare-hook is run in the | ||
| 291 | ;; summary buffer... | ||
| 292 | 'gnus-summary-mode) | ||
| 293 | gnus-article-buffer | ||
| 294 | (buffer-live-p (get-buffer gnus-article-buffer))) | ||
| 295 | (with-current-buffer gnus-article-buffer | ||
| 296 | (catch 'setup-done | ||
| 297 | ;; Copy over the values from the summary buffer. | ||
| 298 | (when (and gnus-summary-buffer | ||
| 299 | (buffer-live-p gnus-summary-buffer)) | ||
| 300 | (setq-local bug-reference-bug-regexp | ||
| 301 | (with-current-buffer gnus-summary-buffer | ||
| 302 | bug-reference-bug-regexp)) | ||
| 303 | (setq-local bug-reference-url-format | ||
| 304 | (with-current-buffer gnus-summary-buffer | ||
| 305 | bug-reference-url-format)) | ||
| 306 | (when (and bug-reference-bug-regexp | ||
| 307 | bug-reference-url-format) | ||
| 308 | (throw 'setup-done t))) | ||
| 309 | ;; If the summary had no values, try setting according to | ||
| 310 | ;; the values of the From, To, and Cc headers. | ||
| 311 | (let ((headers (save-excursion | ||
| 312 | (goto-char (point-min)) | ||
| 313 | (mail-header-extract))) | ||
| 314 | header-values) | ||
| 315 | (dolist (h '(list-id to from cc)) | ||
| 316 | (let ((val (mail-header h headers))) | ||
| 317 | (when val | ||
| 318 | (push val header-values)))) | ||
| 319 | (bug-reference--maybe-setup-from-mail | ||
| 320 | nil header-values))))))) | ||
| 321 | |||
| 322 | (defun bug-reference--after-hook () | ||
| 323 | (when (or bug-reference-mode | ||
| 324 | bug-reference-prog-mode) | ||
| 325 | ;; Automatic setup only if the variables aren't already set, e.g., | ||
| 326 | ;; by a local variables section in the file. | ||
| 327 | (unless (and bug-reference-bug-regexp | ||
| 328 | bug-reference-url-format) | ||
| 329 | (with-demoted-errors | ||
| 330 | "Error during bug-reference auto-setup: %S" | ||
| 331 | (catch 'setup | ||
| 332 | (dolist (f (list #'bug-reference-try-setup-from-vc | ||
| 333 | #'bug-reference-try-setup-from-gnus)) | ||
| 334 | (when (funcall f) | ||
| 335 | (throw 'setup t)))))))) | ||
| 336 | |||
| 142 | ;;;###autoload | 337 | ;;;###autoload |
| 143 | (define-minor-mode bug-reference-mode | 338 | (define-minor-mode bug-reference-mode |
| 144 | "Toggle hyperlinking bug references in the buffer (Bug Reference mode)." | 339 | "Toggle hyperlinking bug references in the buffer (Bug Reference mode)." |
| 145 | nil | 340 | nil |
| 146 | "" | 341 | "" |
| 147 | nil | 342 | nil |
| 343 | :after-hook (bug-reference--after-hook) | ||
| 148 | (if bug-reference-mode | 344 | (if bug-reference-mode |
| 149 | (jit-lock-register #'bug-reference-fontify) | 345 | (jit-lock-register #'bug-reference-fontify) |
| 150 | (jit-lock-unregister #'bug-reference-fontify) | 346 | (jit-lock-unregister #'bug-reference-fontify) |
| @@ -158,6 +354,7 @@ The second subexpression should match the bug reference (usually a number)." | |||
| 158 | nil | 354 | nil |
| 159 | "" | 355 | "" |
| 160 | nil | 356 | nil |
| 357 | :after-hook (bug-reference--after-hook) | ||
| 161 | (if bug-reference-prog-mode | 358 | (if bug-reference-prog-mode |
| 162 | (jit-lock-register #'bug-reference-fontify) | 359 | (jit-lock-register #'bug-reference-fontify) |
| 163 | (jit-lock-unregister #'bug-reference-fontify) | 360 | (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 | ||
| 970 | If FILE is already registered, return the | 970 | If FILE is already registered, return the |
| @@ -974,7 +974,10 @@ responsible for FILE is returned. | |||
| 974 | 974 | ||
| 975 | Note that if FILE is a symbolic link, it will not be resolved -- | 975 | Note that if FILE is a symbolic link, it will not be resolved -- |
| 976 | the responsible backend system for the symbolic link itself will | 976 | the responsible backend system for the symbolic link itself will |
| 977 | be reported." | 977 | be reported. |
| 978 | |||
| 979 | If NO-ERROR is nil, signal an error that no VC backend is | ||
| 980 | responsible 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. |