mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-02-10 09:43:26 -08:00
bug-reference-bug-regexp now defines a contract for the overlay region
Formerly, bug-reference-fontify placed the overlay on the complete match of bug-reference-bug-regexp. That made it impossible to encode constraints like "must not match at BOL" in the regexp without messing up fontification. Therefore, now it establishes the contract that subexpression 1 defines the overlay region. Subexpression 2 must still match the part of the bug reference injected into bug-reference-url-format if that's a string. If its a function, the interpretation of subexpressions > 1 is up to the function. For backwards compatibility, bug-reference-fontify checks if the bounds of subexpression 2..10 are within the bounds of subexpession 1. If not, or subexpression 1 doesn't even exist/match, we fall back to placing the overlay from (match-beginning 0) to (match-end 0) but issue a warning. * lisp/progmodes/bug-reference.el (bug-reference-bug-regexp): Document contract that subexpression 1 defines the overlay region and adapt the default value accordingly. (bug-reference--nonconforming-regexps): New internal variable. (bug-reference--overlay-bounds): New function. (bug-reference-fontify): Place overlay on subexpression 1's bounds if bug-reference-bug-regexp conforms to the documented contract. (bug-reference--setup-from-vc-alist): Adapt regexps to new contract. * doc/emacs/maintaining.texi (Bug Reference): Adapt regexp used in example.
This commit is contained in:
parent
bfd5e268a8
commit
ccc9bd774c
2 changed files with 83 additions and 40 deletions
|
|
@ -3108,7 +3108,7 @@ these local variables section would do.
|
|||
|
||||
@smallexample
|
||||
;; Local Variables:
|
||||
;; bug-reference-bug-regexp: "\\([Bb]ug[#-]\\)\\([0-9]+\\)"
|
||||
;; bug-reference-bug-regexp: "\\([Bb]ug[#-]\\([0-9]+\\)\\)"
|
||||
;; bug-reference-url-format: "https://project.org/issues/%s"
|
||||
;; End:
|
||||
@end smallexample
|
||||
|
|
@ -3118,9 +3118,9 @@ The string captured by the second regexp group in
|
|||
template in the @code{bug-reference-url-format}.
|
||||
|
||||
Note that @code{bug-reference-url-format} may also be a function in
|
||||
order to cater for more complex scenarios, e.g., when the part before
|
||||
the actual bug number has to be used to distinguish between issues and
|
||||
merge requests where each of them has a different URL.
|
||||
order to cater for more complex scenarios, e.g., when different parts
|
||||
of the bug reference have to be used to distinguish between issues and
|
||||
merge requests resulting in different URLs.
|
||||
|
||||
|
||||
@heading Automatic Setup
|
||||
|
|
|
|||
|
|
@ -72,24 +72,30 @@ so that it is considered safe, see `enable-local-variables'.")
|
|||
(get s 'bug-reference-url-format)))))
|
||||
|
||||
(defcustom bug-reference-bug-regexp
|
||||
"\\([Bb]ug ?#?\\|[Pp]atch ?#\\|RFE ?#\\|PR [a-z+-]+/\\)\\([0-9]+\\(?:#[0-9]+\\)?\\)"
|
||||
"\\(\\(?:[Bb]ug ?#?\\|[Pp]atch ?#\\|RFE ?#\\|PR [a-z+-]+/\\)\\([0-9]+\\(?:#[0-9]+\\)?\\)\\)"
|
||||
"Regular expression matching bug references.
|
||||
The second subexpression should match the bug reference (usually
|
||||
a number).
|
||||
The first subexpression defines the region of the bug-reference
|
||||
overlay, i.e., the region being fontified and made clickable in
|
||||
order to browse the referenced bug in the corresponding project's
|
||||
issue tracker.
|
||||
|
||||
The complete expression's matches will be highlighted unless
|
||||
there is a 99th subexpression. In that case, only the matches of
|
||||
that will be highlighted. For example, this can be used to
|
||||
define that bug references at the beginning of a line must not be
|
||||
matched by using a regexp like
|
||||
If `bug-reference-url-format' is set to a format string with
|
||||
single %s placeholder, the second subexpression must match
|
||||
the (part of the) bug reference which needs to be injected in
|
||||
place of the %s in order to form the bug's ticket URL.
|
||||
|
||||
\"[^\\n]\\\\(?99:\\\\([Bb]ug ?\\\\)\\\\(#[0-9]+\\\\)\\\\)\"
|
||||
|
||||
If there wasn't this explicitly numbered group 99, the
|
||||
non-newline character before the actual bug reference would be
|
||||
highlighted, too."
|
||||
If `bug-reference-url-format' is a function, the interpretation
|
||||
of the subexpressions larger than 1 is up to the function.
|
||||
However, it is checked that the bounds of all matching
|
||||
subexpressions from 2 to 10 are within the bounds of the
|
||||
subexpression 1 defining the overlay region. Larger
|
||||
subexpressions may also be used by the function but may lay
|
||||
outside the bounds of subexpressions 1 and then don't contribute
|
||||
to the highlighted and clickable region."
|
||||
:type 'regexp
|
||||
:version "24.3") ; previously defconst
|
||||
; 24.3: defconst -> defcustom
|
||||
; 28.1: contract about subexpression 1 defines the overlay region.
|
||||
:version "28.1")
|
||||
|
||||
;;;###autoload
|
||||
(put 'bug-reference-bug-regexp 'safe-local-variable 'stringp)
|
||||
|
|
@ -119,6 +125,48 @@ highlighted, too."
|
|||
|
||||
(defvar bug-reference-prog-mode)
|
||||
|
||||
(defvar bug-reference--nonconforming-regexps nil
|
||||
"Holds `bug-reference-bug-regexp' values which don't conform to
|
||||
the documented contract in order to warn about their
|
||||
non-conformance only once.")
|
||||
|
||||
(defun bug-reference--overlay-bounds ()
|
||||
(let ((m-b1 (match-beginning 1))
|
||||
(m-e1 (match-end 1)))
|
||||
(if (and m-b1 m-e1
|
||||
(catch 'within-bounds
|
||||
(let ((i 2))
|
||||
(while (<= i 10)
|
||||
(when (and (match-beginning i)
|
||||
(or (< (match-beginning i) m-b1)
|
||||
(> (match-end i) m-e1)))
|
||||
(throw 'within-bounds nil))
|
||||
(cl-incf i))
|
||||
t)))
|
||||
;; All groups 2..10 are within bounds.
|
||||
(cons m-b1 m-e1)
|
||||
;; The regexp doesn't fulfil the contract of
|
||||
;; bug-reference-bug-regexp, so fall back to the old behavior.
|
||||
(unless (member bug-reference-bug-regexp
|
||||
bug-reference--nonconforming-regexps)
|
||||
(setq bug-reference--nonconforming-regexps
|
||||
(cons bug-reference-bug-regexp
|
||||
bug-reference--nonconforming-regexps))
|
||||
(display-warning
|
||||
'bug-reference
|
||||
(format-message
|
||||
"The value of `bug-reference-bug-regexp'
|
||||
|
||||
%S
|
||||
|
||||
in buffer %S doesn't conform to the contract specified by its
|
||||
docstring. The subexpression 1 should define the region of the
|
||||
bug-reference overlay and cover all other subexpressions up to
|
||||
subexpression 10."
|
||||
bug-reference-bug-regexp
|
||||
(buffer-name))))
|
||||
(cons (match-beginning 0) (match-end 0)))))
|
||||
|
||||
(defun bug-reference-fontify (start end)
|
||||
"Apply bug reference overlays to the region between START and END."
|
||||
(save-excursion
|
||||
|
|
@ -132,19 +180,14 @@ highlighted, too."
|
|||
(when (or (not bug-reference-prog-mode)
|
||||
;; This tests for both comment and string syntax.
|
||||
(nth 8 (syntax-ppss)))
|
||||
;; We highlight the 99th subexpression if that exists,
|
||||
;; otherwise the complete match. See the docstring of
|
||||
;; `bug-reference-bug-regexp'.
|
||||
(let* ((s (or (match-beginning 99)
|
||||
(match-beginning 0)))
|
||||
(e (or (match-end 99)
|
||||
(match-end 0)))
|
||||
(let* ((bounds (bug-reference--overlay-bounds))
|
||||
(overlay (or
|
||||
(let ((ov (pop overlays)))
|
||||
(when ov
|
||||
(move-overlay ov s e)
|
||||
(move-overlay ov (car bounds) (cdr bounds))
|
||||
ov))
|
||||
(let ((ov (make-overlay s e nil t nil)))
|
||||
(let ((ov (make-overlay (car bounds) (cdr bounds)
|
||||
nil t nil)))
|
||||
(overlay-put ov 'category 'bug-reference)
|
||||
ov))))
|
||||
;; Don't put a link if format is undefined.
|
||||
|
|
@ -232,7 +275,7 @@ for the known free software forges from the variables
|
|||
;; `bug-reference-url-format' and
|
||||
;; `bug-reference-bug-regexp' aren't set already.
|
||||
("git\\.\\(?:sv\\|savannah\\)\\.gnu\\.org:"
|
||||
"\\<\\([Bb]ug ?#?\\)\\([0-9]+\\(?:#[0-9]+\\)?\\)\\>"
|
||||
"\\<\\(\\(?:[Bb]ug ?#?\\)\\([0-9]+\\(?:#[0-9]+\\)?\\)\\)\\>"
|
||||
,(lambda (_) "https://debbugs.gnu.org/%s"))
|
||||
;;
|
||||
;; GitHub projects.
|
||||
|
|
@ -243,17 +286,17 @@ for the known free software forges from the variables
|
|||
;; user/project#17 links to possibly different projects
|
||||
;; are also supported.
|
||||
("[/@]github.com[/:]\\([.A-Za-z0-9_/-]+\\)\\.git"
|
||||
"\\([.A-Za-z0-9_/-]+\\)?\\(?:#\\)\\([0-9]+\\)\\>"
|
||||
"\\(\\([.A-Za-z0-9_/-]+\\)?\\(?:#\\)\\([0-9]+\\)\\)\\>"
|
||||
,(lambda (groups)
|
||||
(let ((ns-project (nth 1 groups)))
|
||||
(lambda ()
|
||||
(concat "https://github.com/"
|
||||
(or
|
||||
;; Explicit user/proj#18 link.
|
||||
(match-string 1)
|
||||
(match-string 2)
|
||||
ns-project)
|
||||
"/issues/"
|
||||
(match-string 2))))))
|
||||
(match-string 3))))))
|
||||
;;
|
||||
;; Gitea instances.
|
||||
;;
|
||||
|
|
@ -261,7 +304,7 @@ for the known free software forges from the variables
|
|||
(,(concat "[/@]"
|
||||
(regexp-opt bug-reference-gitea-instances t)
|
||||
"[/:]\\([.A-Za-z0-9_/-]+\\)\\.git")
|
||||
"\\([.A-Za-z0-9_/-]+\\)?\\(?:#\\)\\([0-9]+\\)\\>"
|
||||
"\\(\\([.A-Za-z0-9_/-]+\\)?\\(?:#\\)\\([0-9]+\\)\\)\\>"
|
||||
,(lambda (groups)
|
||||
(let ((host (nth 1 groups))
|
||||
(ns-project (nth 2 groups)))
|
||||
|
|
@ -269,10 +312,10 @@ for the known free software forges from the variables
|
|||
(concat "https://" host "/"
|
||||
(or
|
||||
;; Explicit user/proj#18 link.
|
||||
(match-string 1)
|
||||
(match-string 2)
|
||||
ns-project)
|
||||
"/issues/"
|
||||
(match-string 2))))))
|
||||
(match-string 3))))))
|
||||
;;
|
||||
;; GitLab instances.
|
||||
;;
|
||||
|
|
@ -283,19 +326,19 @@ for the known free software forges from the variables
|
|||
(,(concat "[/@]"
|
||||
(regexp-opt bug-reference-gitlab-instances t)
|
||||
"[/:]\\([.A-Za-z0-9_/-]+\\)\\.git")
|
||||
"\\(?1:[.A-Za-z0-9_/-]+\\)?\\(?3:[#!]\\)\\(?2:[0-9]+\\)\\>"
|
||||
"\\(\\([.A-Za-z0-9_/-]+\\)?\\([#!]\\)\\([0-9]+\\)\\)\\>"
|
||||
,(lambda (groups)
|
||||
(let ((host (nth 1 groups))
|
||||
(ns-project (nth 2 groups)))
|
||||
(lambda ()
|
||||
(concat "https://" host "/"
|
||||
(or (match-string 1)
|
||||
(or (match-string 2)
|
||||
ns-project)
|
||||
"/-/"
|
||||
(if (string= (match-string 3) "#")
|
||||
"issues/"
|
||||
"merge_requests/")
|
||||
(match-string 2))))))
|
||||
(match-string 4))))))
|
||||
;;
|
||||
;; Sourcehut instances.
|
||||
;;
|
||||
|
|
@ -311,7 +354,7 @@ for the known free software forges from the variables
|
|||
(,(concat "[/@]\\(?:git\\|hg\\)."
|
||||
(regexp-opt bug-reference-sourcehut-instances t)
|
||||
"[/:]\\(~[.A-Za-z0-9_/-]+\\)")
|
||||
"\\(~[.A-Za-z0-9_/-]+\\)?\\(?:#\\)\\([0-9]+\\)\\>"
|
||||
"\\(\\(~[.A-Za-z0-9_/-]+\\)?\\(?:#\\)\\([0-9]+\\)\\)\\>"
|
||||
,(lambda (groups)
|
||||
(let ((host (nth 1 groups))
|
||||
(ns-project (nth 2 groups)))
|
||||
|
|
@ -319,10 +362,10 @@ for the known free software forges from the variables
|
|||
(concat "https://todo." host "/"
|
||||
(or
|
||||
;; Explicit user/proj#18 link.
|
||||
(match-string 1)
|
||||
(match-string 2)
|
||||
ns-project)
|
||||
"/"
|
||||
(match-string 2))))))))))
|
||||
(match-string 3))))))))))
|
||||
|
||||
(defvar bug-reference-setup-from-vc-alist nil
|
||||
"An alist for setting up `bug-reference-mode' based on VC URL.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue