From c4adfbae24d920f0ce62cb88b988219348d1ec73 Mon Sep 17 00:00:00 2001 From: Tassilo Horn Date: Mon, 4 May 2020 11:24:08 +0200 Subject: [PATCH] Allow for custom URL handlers in browse-url. * lisp/net/browse-url.el (browse-url-handlers): New defcustom. (browse-url-default-handlers): New defvar. (browse-url): Use them. Adapt docstring. Issue a warning pointing to browse-url-handlers when browse-url-browser-function is an alist. (browse-url--mailto, browse-url--man): New functions. (browse-url--browser-defcustom-type): Add :doc that the alist usage is deprecated. (browse-url-browser-function): Remove documentation referring to the alist usage and mention browse-url-handlers. * doc/emacs/misc.texi: Document browse-url-handlers in Browse-URL node. * etc/NEWS: Mention browse-url-default-handlers and browse-url-handlers. --- doc/emacs/misc.texi | 13 +++-- etc/NEWS | 16 ++++++ lisp/net/browse-url.el | 114 ++++++++++++++++++++++++++++------------- 3 files changed, 104 insertions(+), 39 deletions(-) diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi index 47f195d0b20..d1854f31ff3 100644 --- a/doc/emacs/misc.texi +++ b/doc/emacs/misc.texi @@ -2920,9 +2920,16 @@ you might like to bind to keys, such as @code{browse-url-at-point} and You can customize Browse-URL's behavior via various options in the @code{browse-url} Customize group. In particular, the option @code{browse-url-mailto-function} lets you define how to follow -@samp{mailto:} URLs, while @code{browse-url-browser-function} lets you -define how to follow other types of URLs. For more information, view -the package commentary by typing @kbd{C-h P browse-url @key{RET}}. +@samp{mailto:} URLs, while @code{browse-url-browser-function} +specifies your default browser. + +@vindex browse-url-handlers + You can define that certain URLs are browsed with other functions by +customizing @code{browse-url-handlers}, an alist of regular +expressions paired with functions to browse matching URLs. + +For more information, view the package commentary by typing @kbd{C-h P +browse-url @key{RET}}. @node Goto Address mode @subsection Activating URLs diff --git a/etc/NEWS b/etc/NEWS index 0f4b6244c62..ac93a76ff95 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -304,6 +304,22 @@ use ‘doxygen’ by default one might evaluate: (c++-mode . doxygen))) or use it in a custom ‘c-style’. + +** browse-url + +*** Added support for custom URL handlers + +There is a new defvar 'browse-url-default-handlers' and a defcustom +'browse-url-handlers' being alists with (REGEXP . FUNCTION) entries +allowing to define different browsing FUNCTIONs depending on the URL +to be browsed. The defvar is for default handlers provided by Emacs +itself or external packages, the defcustom is for the user (and allows +for overriding the default handlers). + +Formerly, one could do the same by setting +'browse-url-browser-function' to such an alist. This usage is still +supported but deprecated. + * New Modes and Packages in Emacs 28.1 diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el index 7aad44b2876..1275c15578f 100644 --- a/lisp/net/browse-url.el +++ b/lisp/net/browse-url.el @@ -114,9 +114,10 @@ ;; To always save modified buffers before displaying the file in a browser: ;; (setq browse-url-save-file t) -;; To invoke different browsers for different URLs: -;; (setq browse-url-browser-function '(("^mailto:" . browse-url-mail) -;; ("." . browse-url-firefox))) +;; To invoke different browsers/tools for different URLs, customize +;; `browse-url-handlers'. In earlier versions of Emacs, the same +;; could be done by setting `browse-url-browser-function' to an alist +;; but this usage is deprecated now. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Code: @@ -157,7 +158,9 @@ :value browse-url-default-browser) (function :tag "Your own function") (alist :tag "Regexp/function association list" - :key-type regexp :value-type function))) + :key-type regexp :value-type function + :format "%{%t%}\n%d%v\n" + :doc "Deprecated. Use `browse-url-handlers' instead."))) ;;;###autoload (defcustom browse-url-browser-function 'browse-url-default-browser @@ -165,13 +168,8 @@ This is used by the `browse-url-at-point', `browse-url-at-mouse', and `browse-url-of-file' commands. -If the value is not a function it should be a list of pairs -\(REGEXP . FUNCTION). In this case the function called will be the one -associated with the first REGEXP which matches the current URL. The -function is passed the URL and any other args of `browse-url'. The last -regexp should probably be \".\" to specify a default browser. - -Also see `browse-url-secondary-browser-function'." +Also see `browse-url-secondary-browser-function' and +`browse-url-handlers'." :type browse-url--browser-defcustom-type :version "24.1") @@ -595,6 +593,41 @@ down (this *won't* always work)." "Wrapper command prepended to the Elinks command-line." :type '(repeat (string :tag "Wrapper"))) +(defun browse-url--mailto (url &rest args) + "Calls `browse-url-mailto-function' with URL and ARGS." + (funcall browse-url-mailto-function url args)) + +(defun browse-url--man (url &rest args) + "Calls `browse-url-man-function' with URL and ARGS." + (funcall browse-url-man-function url args)) + +;;;###autoload +(defvar browse-url-default-handlers + '(("\\`mailto:" . browse-url--mailto) + ("\\`man:" . browse-url--man) + ("\\`file://" . browse-url-emacs)) + "Like `browse-url-handlers' but populated by Emacs and packages. + +Emacs and external packages capable of browsing certain URLs +should place their entries in this alist rather than +`browse-url-handlers' which is reserved for the user.") + +(defcustom browse-url-handlers nil + "An alist with elements of the form (REGEXP HANDLER). +Each REGEXP is matched against the URL to be opened in turn and +the first match's HANDLER is invoked with the URL. + +A HANDLER must be a function with the same arguments as +`browse-url'. + +If no REGEXP matches, the same procedure is performed with the +value of `browse-url-default-handlers'. If there is also no +match, the URL is opened using the value of +`browse-url-browser-function'." + :type '(alist :key-type (regexp :tag "Regexp") + :value-type (function :tag "Handler")) + :version "28.1") + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; URL encoding @@ -768,16 +801,18 @@ narrowed." "Ask a WWW browser to load URL. Prompt for a URL, defaulting to the URL at or before point. Invokes a suitable browser function which does the actual job. -The variable `browse-url-browser-function' says which browser function to -use. If the URL is a mailto: URL, consult `browse-url-mailto-function' -first, if that exists. - -The additional ARGS are passed to the browser function. See the doc -strings of the actual functions, starting with `browse-url-browser-function', -for information about the significance of ARGS (most of the functions -ignore it). -If ARGS are omitted, the default is to pass `browse-url-new-window-flag' -as ARGS." + +The variables `browse-url-browser-function', +`browse-url-handlers', and `browse-url-default-handlers' +determine which browser function to use. + +The additional ARGS are passed to the browser function. See the +doc strings of the actual functions, starting with +`browse-url-browser-function', for information about the +significance of ARGS (most of the functions ignore it). + +If ARGS are omitted, the default is to pass +`browse-url-new-window-flag' as ARGS." (interactive (browse-url-interactive-arg "URL: ")) (unless (called-interactively-p 'interactive) (setq args (or args (list browse-url-new-window-flag)))) @@ -786,12 +821,15 @@ as ARGS." (not (string-match "\\`[a-z]+:" url))) (setq url (expand-file-name url))) (let ((process-environment (copy-sequence process-environment)) - (function (or (and (string-match "\\`mailto:" url) - browse-url-mailto-function) - (and (string-match "\\`man:" url) - browse-url-man-function) - browse-url-browser-function)) - ;; Ensure that `default-directory' exists and is readable (b#6077). + (function + (catch 'custom-url-handler + (dolist (regex-handler (append browse-url-handlers + browse-url-default-handlers)) + (when (string-match-p (car regex-handler) url) + (throw 'custom-url-handler (cdr regex-handler)))) + ;; No special handler found. + browse-url-browser-function)) + ;; Ensure that `default-directory' exists and is readable (bug#6077). (default-directory (or (unhandled-file-name-directory default-directory) (expand-file-name "~/")))) ;; When connected to various displays, be careful to use the display of @@ -801,15 +839,19 @@ as ARGS." (setenv "DISPLAY" (frame-parameter nil 'display))) (if (and (consp function) (not (functionp function))) - ;; The `function' can be an alist; look down it for first match - ;; and apply the function (which might be a lambda). - (catch 'done - (dolist (bf function) - (when (string-match (car bf) url) - (apply (cdr bf) url args) - (throw 'done t))) - (error "No browse-url-browser-function matching URL %s" - url)) + ;; The `function' can be an alist; look down it for first + ;; match and apply the function (which might be a lambda). + ;; However, this usage is deprecated as of Emacs 28.1. + (progn + (warn "Having `browse-url-browser-function' set to an +alist is deprecated. Use `browse-url-handlers' instead.") + (catch 'done + (dolist (bf function) + (when (string-match (car bf) url) + (apply (cdr bf) url args) + (throw 'done t))) + (error "No browse-url-browser-function matching URL %s" + url))) ;; Unbound symbols go down this leg, since void-function from ;; apply is clearer than wrong-type-argument from dolist. (apply function url args)))) -- 2.39.5