Endless Parentheses

Ramblings on productivity and technical subjects.

profile for Malabarba on Stack Exchange

Intelligent browse-url

When you have more than one browser installed, you must choose which one's the default. The OS will then proceed to open all URLs in the default browser, even if it happens to be closed while another one happens to be running.

At least in Emacs we can fix that behavior. The following code configures Emacs to use whatever browser happens to be open right now, instead of always defaulting to the same one.

;;; We don't need to `require' because this function is only
;;; ever called by `browse-url' itself. But if you have
;;; problems, try uncommenting this:
;; (require 'browse-url)
(defun endless/browse-url-best-browser (url &rest _)
  "Use a running browser or start the preferred one."
  (setq url (browse-url-encode-url url))
  (let ((process-environment
        (command (endless/decide-browser)))
    (start-process (concat command " " url)
                   nil command url)))

;; Use this for all links. If you actually don't want some
;; links to be viewed externally, change this line here.
(setq browse-url-browser-function
      '(("." . endless/browse-url-best-browser)))

Here we configure our preferences through the endless/browser-list variable. Its value is a list of cons cells, each representing a browser. The car is a regexp to match the name of the browser's process (used to determine whether the browser is running), and the cdr is the name of the executable.

(defcustom endless/browser-list
  '(("xulrunner\\|conkeror" . "conkeror.sh")
    ("xulrunner\\|conkeror" . "conkeror")
    "conkeror.exe" ;; This works if it's in your $PATH
    ("chrome$" . "google-chrome-stable")
    ("firefox\\|mozilla" . "firefox")
    ;; This works regardless of your $PATH
    ("firefox.exe" .
     "c:/Program Files (x86)/Mozilla Firefox/firefox.exe"))
  "List of browsers by order of preference.
Each element is a cons cell (REGEXP . EXEC-FILE).
If REGEXP matches the name of a currently running process and if
EXEC-FILE a valid executable, EXEC-FILE will be used to open the
given URL.

An element can also be a string, in this case, it is used as both
the REGEXP and the EXEC-FILE.

It is safe to have items referring to not-installed browsers,
they are gracefully ignored."
  :type '(repeat (choice string (cons regexp file))))

And this is the function responsible for checking the running processes and finding a browser in there.

(defun endless/decide-browser ()
  "Decide best browser to use based on `endless/browser-list'."
  (let ((process-list
         (mapcar #'endless/process-name
        (browser-list endless/browser-list)
        browser out)
    ;; Find the first browser on the list that is open.
    (while (and browser-list (null out))
      (setq browser (car browser-list))
      (if (and (cl-member (car-or-self browser)
                          process-list :test 'string-match)
               (executable-find (cdr-or-self browser)))
          (setq out (cdr-or-self browser))
        (setq browser-list (cdr browser-list))))
    ;; Use the one we found, or the first one available.
    (or out (endless/first-existing-browser))))

(defun endless/first-existing-browser ()
  "Return the first installed browser in `endless/browser-list'."
  (require 'cl-lib)
     (lambda (x) (executable-find (cdr-or-self x)))

(defun endless/process-name (proc)
  (cdr (assoc 'comm (process-attributes proc))))

(defun car-or-self (x)
  "If X is a list, return the car. Otherwise, return X."
  (or (car-safe x) x))

(defun cdr-or-self (x)
  "If X is a list, return the cdr. Otherwise, return X."
  (or (cdr-safe x) x))

I've tested it on Windows and a couple of Linux distros. Could a Mac user test it for me as well?

comments powered by Disqus