blob: fddc02e6a2fb722cd14d3faab7fee68c524e8935 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
;;; browser-config.el --- Browser Configuration -*- lexical-binding: t; coding: utf-8; -*-
;; author: Craig Jennings <c@cjennings.net>
;;; Commentary:
;; This module provides browser selection and configuration for Emacs.
;; It automatically discovers available browsers on the system, allows the user
;; to choose their preferred browser via completing-read, and persists the choice
;; to a file. Works with all link types including org-mode links.
;;
;; Interactive Commands:
;; M-x cj/choose-browser - Select default browser from available options
;;; Code:
(require 'cl-lib)
;; Persistence file for storing browser choice
(defvar cj/browser-choice-file
(expand-file-name "browser-choice.el" user-emacs-directory)
"File to persist the user's browser choice.")
;; Browser definitions: (executable-name browse-function display-name [program-var])
;; Use nil for executable-name to indicate a built-in Emacs browser (always available)
(defvar cj/browser-definitions
'((nil eww-browse-url "EWW (Emacs Browser)" nil)
("google-chrome" browse-url-chrome "Google Chrome" browse-url-chrome-program)
("google-chrome-stable" browse-url-chrome "Google Chrome" browse-url-chrome-program)
("chrome" browse-url-chrome "Chrome" browse-url-chrome-program)
("chromium" browse-url-chromium "Chromium" browse-url-chromium-program)
("chromium-browser" browse-url-chromium "Chromium" browse-url-chromium-program)
("firefox" browse-url-firefox "Firefox" browse-url-firefox-program)
("brave" browse-url-chrome "Brave" browse-url-chrome-program)
("brave-browser" browse-url-chrome "Brave Browser" browse-url-chrome-program)
("microsoft-edge" browse-url-chrome "Microsoft Edge" browse-url-chrome-program)
("vivaldi" browse-url-chrome "Vivaldi" browse-url-chrome-program)
("opera" browse-url-chrome "Opera" browse-url-chrome-program)
("qutebrowser" browse-url-generic "qutebrowser" browse-url-generic-program))
"List of browser definitions.
Each entry is (EXECUTABLE BROWSE-FUNCTION DISPLAY-NAME [PROGRAM-VAR]).
Use nil for EXECUTABLE to indicate a built-in Emacs browser (always available).")
(defun cj/discover-browsers ()
"Discover available browsers on the system.
Returns a list of plists with :executable, :function, :name, and :path.
Includes built-in Emacs browsers (those with nil executable)."
(let ((found-browsers '())
(seen-names (make-hash-table :test 'equal)))
(dolist (def cj/browser-definitions)
(let* ((executable (nth 0 def))
(browse-fn (nth 1 def))
(display-name (nth 2 def))
(program-var (nth 3 def))
(path (when executable (executable-find executable)))
;; Built-in browsers (nil executable) are always available
(available-p (or (null executable) path)))
(when (and available-p (not (gethash display-name seen-names)))
(puthash display-name t seen-names)
(push (list :executable executable
:function browse-fn
:name display-name
:path path
:program-var program-var)
found-browsers))))
(nreverse found-browsers)))
(defun cj/save-browser-choice (browser-plist)
"Save BROWSER-PLIST to the persistence file."
(with-temp-file cj/browser-choice-file
(insert ";;; Browser choice - Auto-generated\n")
(insert (format "(setq cj/saved-browser-choice '%S)\n" browser-plist))))
(defun cj/load-browser-choice ()
"Load browser choice from the persistence file.
Returns the browser plist if found, nil otherwise."
(when (file-exists-p cj/browser-choice-file)
(condition-case nil
(progn
(load cj/browser-choice-file)
(when (boundp 'cj/saved-browser-choice)
cj/saved-browser-choice))
(error nil))))
(defun cj/apply-browser-choice (browser-plist)
"Apply the browser settings from BROWSER-PLIST."
(when browser-plist
(let ((browse-fn (plist-get browser-plist :function))
(executable (plist-get browser-plist :executable))
(path (plist-get browser-plist :path))
(program-var (plist-get browser-plist :program-var)))
;; Set the main browse-url function
(setq browse-url-browser-function browse-fn)
;; Set the specific browser program variable if it exists
(when program-var
(set program-var (or path executable)))
(message "Default browser set to: %s" (plist-get browser-plist :name)))))
(defun cj/choose-browser ()
"Interactively choose a browser from available options.
Persists the choice for future sessions."
(interactive)
(let* ((browsers (cj/discover-browsers))
(choices (mapcar (lambda (b) (plist-get b :name)) browsers)))
(if (null browsers)
(message "No supported browsers found on system PATH")
(let* ((choice (completing-read "Choose default browser: " choices nil t))
(selected (cl-find-if (lambda (b)
(string= (plist-get b :name) choice))
browsers)))
(when selected
(cj/save-browser-choice selected)
(cj/apply-browser-choice selected))))))
;; Initialize: Load saved choice or use first available browser
(defun cj/initialize-browser ()
"Initialize browser configuration on startup."
(let ((saved-choice (cj/load-browser-choice)))
(if saved-choice
(cj/apply-browser-choice saved-choice)
;; No saved choice - try to set first available browser silently
(let ((browsers (cj/discover-browsers)))
(when browsers
(cj/apply-browser-choice (car browsers))
(message "No browser configured. Using %s. Run M-x cj/choose-browser to change."
(plist-get (car browsers) :name)))))))
;; Run initialization
(cj/initialize-browser)
(keymap-global-set "C-; B" #'cj/choose-browser)
(provide 'browser-config)
;;; browser-config.el ends here
|