;;; +browse-url.el -*- lexical-binding: t -*- (require 'browse-url) (cl-defmacro +browse-url-make-external-viewer-handler (viewer default-args &optional (prompt "URL: ") &key (custom-group 'browse-url) (name (intern (format "+browse-url-with-%s" viewer))) doc vardoc (varname (intern (format "%s-args" name))) (fallback t)) "Create a `browse-url' handler function calling VIEWER on the url. This macro also creates a `customize' setting in CUSTOM-GROUP for VIEWER's command-line arguments. DEFAULT-ARGS specifies the default arguments for that setting. PROMPT is shown to the user in the function's `interactive' spec, as an argument to `browse-url-interactive-arg'. The resulting function is named NAME, which defaults to `+browse-url-wth-VIEWER'. The custom variable is named VARNAME, which defaults to `NAME-args'. If DOC or VARDOC are provided, they'll be the documentation of the function and variable respectively; otherwise, basic docstrings are used. Finally, if FALLBACK is non-nil (by default, it's `browse-url-generic'), the function will call that if unable to start VIEWER." (declare (indent 1)) `(progn (defcustom ,varname ,default-args ,(or doc (format "Arguments to pass to %s in `%s'." viewer name)) :type '(repeat :tag "Command-line argument" string) :group ',custom-group) (defun ,name (url &optional new-window) ,(or vardoc (format "Open URL in %s." viewer)) (interactive (browse-url-interactive-arg ,prompt)) (let* ((url (browse-url-encode-url url)) (process-environment (browse-url-process-environment))) (message ,(format "Opening %%s in %s..." viewer) url) (unless (ignore-errors (apply #'start-process (format "%s %s" ,viewer url) nil ,viewer (append ,varname (list url)))) ,@(cond ((eq fallback t) '((browse-url-generic url new-window))) (fallback `((funcall ,fallback url new-window))) (:else `((message "Can't find viewer: `%s'" ,viewer) nil)))))))) (defcustom +browse-url-download-open t "Whether to open downloaded files afterward." :group 'browse-url :type 'boolean) (defun +browse-url-download-callback (status url dir) ;; A slight change to `eww-download-callback' that returns the downloaded ;; filename. (unless (plist-get status :error) (let* ((obj (url-generic-parse-url url)) (path (directory-file-name (car (url-path-and-query obj)))) (file (eww-make-unique-file-name (eww-decode-url-file-name (file-name-nondirectory path)) dir))) (goto-char (point-min)) (re-search-forward "\r?\n\r?\n") (let ((coding-system-for-write 'no-conversion)) (write-region (point) (point-max) file)) (message "Saved %s" file) file))) (defun +browse-url-download (url &rest _) "Download URL to `eww-download-directory'." (interactive "sDownload URL: ") (let ((dir eww-download-directory)) (when (functionp dir) (setq dir (funcall dir))) (make-directory dir :parents) (url-retrieve url (lambda (s u d) (let ((file (+browse-url-download-callback s u d))) (when +browse-url-download-open (browse-url-xdg-open file)))) (list url dir)))) (provide '+browse-url) ;;; +browse-url.el ends here