Home About Archive Tags RSS

Newest posts:

27 sep. 2026

EWW in Emacs 31 would not use browse-url-generic as browse-url-secondary-browser-function

I recently built Emacs 31 from source to get the newest Emacs on Debian. Most things worked like they used to in Emacs 30 except the few things I knew about from reading up on Emacs 31, but for some strange reason, when browsing with eww inside Emacs, I could no longer hit & and get the site to open in LibreWolf. These are the relevant eww settings in my init.el:

(use-package eww                        ; Web browser
  :config
  (setopt browse-url-browser-function 'eww-browse-url
          browse-url-secondary-browser-function 'browse-url-generic
          browse-url-generic-program "librewolf"
  ; ...

I had a look in the manual to see if there was something new about how browse-url-secondary-browser-function should work, but it seems like it should work exactly like before. I then had a look in the Emacs News (C-h C-n), but it did not say anything about this. The documentation for the relevant variables and functions have not changed either.

Since browse-url-secondary-browser-function no longer accepts 'browse-url-generic, I tried giving it a lambda to start-process "librewolf" instead and that worked perfectly.

(use-package eww                        ; Web browser
  :config
  (setopt browse-url-browser-function 'eww-browse-url
          browse-url-secondary-browser-function (lambda () (start-process "librewolf"))
  ; ...

Another thing that does work is when I do this:

(use-package eww                        ; Web browser
  :config
  (setopt browse-url-browser-function 'eww-browse-url
          browse-url-secondary-browser-function (lambda () (browse-url-generic))
          browse-url-generic-program "librewolf"
  ; ...

When I look up what is bound to & in eww with C-h k, I get eww-browse-with-external-browser.

(defun eww-browse-with-external-browser (&optional url)
  "Browse the current URL with an external browser.
Use `browse-url-secondary-browser-function' if it is an external
browser, otherwise use `browse-url-with-browser-kind' to open an
external browser."
  (interactive nil eww-mode)
  (setq url (or url (plist-get eww-data :url)))
  (if (eq 'external (browse-url--browser-kind
                     browse-url-secondary-browser-function url))
      (let ((browse-url-browser-function browse-url-secondary-browser-function))
        (browse-url url))
    (browse-url-with-browser-kind 'external url)))

When I look into that function's source code, it calls browse-url–browser-kind to determine if the function used for browser-url-secondary-browser-function is an external browser or an internal one and if it is external, uses the function to browse the url by setting browse-url-browser-function temporarily and then use browse-url. If browse-url–browser-kind returns something else, it runs browse-url-with-browser-kind 'external on the url.

When I look at the source for browse-url–browser-kind, it determines if the function used is internal or external by looking at properties set with put for the function. When I looked up browse-url-firefox, after the function definition, there is a line that says (function-put 'browse-url-firefox 'browse-url-browser-kind 'external). If I look up browse-url-generic, I find (function-put 'browse-url-generic 'browse-url-browser-kind 'external) there as well.

When I call browse-url-generic interactively, it works perfectly. When I try to run (browse-url--browser-kind 'browse-url-generic "https://mostad.eu/") it returns external which I also get when running (browse-url--browser-kind browse-url-secondary-browser-function "https://mostad.eu/").

Going back to eww-browse-with-external-browser, when I execute

 (if (eq 'external (browse-url--browser-kind
                     browse-url-secondary-browser-function "https://mostad.eu/"))
      (let ((browse-url-browser-function browse-url-secondary-browser-function))
        (browse-url "https://mostad.eu/"))
    (browse-url-with-browser-kind 'external "https://mostad.eu/"))

it launches mostad.eu in eww, not in librewolf. (This is the part of the function that does the real work, but with url replaced with "https://mostad.eu/" so it works outside the function.) I did a C-x C-e after the if statement and it returned t as expected, so the let should be evaluated. I also tried modifying the else clause to message something if it kicked in and it did not happen.

When I tried the below to see if browse-url-browser-function somehow was something else, then the message buffer showed the even further below output, confirming that it was in fact browse-url-generic.

  (if (eq 'external (browse-url--browser-kind
                     browse-url-secondary-browser-function "https://mostad.eu/"))
      (let ((browse-url-browser-function
             browse-url-secondary-browser-function))
        (message "%s" browse-url-browser-function)
        (browse-url "https://mostad.eu/"))
    (message "neida"))
browse-url-generic
Contacting host: mostad.eu:443
#<buffer  *http mostad.eu:443*>

At this point I was running out of patience and concentration and needed to eat something. Before eating, I remembered when looking through the source code for browse-url-firefox, that it could also open firefox variants if browse-url-firefox-program was set to a firefox downstream, so I tried setting it to "librewolf" and had a go with & in an eww buffer to see if that would work when using & in eww. It did not work. It just reloaded the site I was in in eww.

(use-package eww                        ; Web browser
  :config
  (setopt browse-url-browser-function 'eww-browse-url
          browse-url-secondary-browser-function 'browse-url-firefox
          browse-url-firefox-program "librewolf"
  ; ...

I then reset the setting to browse-url-generic. When trying & in eww, it relaunches the same page in eww. If I set browse-url-secondary-browser-function to any function except the obvious ones like 'browse-url-firefox, 'browse-url-generic etc, for example if I set it to (lambda ()), when I use & in eww, it opens the site in LibreWolf. This seems slightly insane. I went down the rabbit hole and found browse-url-default-browser's source code and it seems to use browse-url-firefox which I previously set up to use librewolf, so maybe that is why this works?

Maybe there is logic saying that browse-url-browser-function and browse-url-secondary-browser-function cannot be the same that stops browse-url from opening the site in librewolf when eww-browse-with-external-browser temporarily sets browse-url-browser-function to browse-url-secondary-browser-function like it does in eww-browse-with-external-browser? I did not see anything like that in the functions I read if I understood them correctly, but I only read some functions and my understanding of Emacs Lisp is still at the beginner level.

I think for now, I will use

(use-package eww                        ; Web browser
  :config
  (setopt browse-url-browser-function 'eww-browse-url
          browse-url-secondary-browser-function (lambda ())
          browse-url-generic-program "librewolf"
  ; ...

which somehow works. I don't know why, but since it works, I'll leave it at that for now.

Is this problem reproducible at all for other people or is it just a problem on my machine? I am on Sway and have set $BROWSER="MOZ_ENABLE_WAYLAND=1 librewolf" in my .bash_profile. I configured Emacs 31 –with-native-compilation=aot –with-cairo –with-harfbuzz –with-gnutls –with-gsettings –with-tree-sitter –with-gif –with-png –with-jpeg –with-rsvg –with-tiff –with-imagemagick –with-pgtk –without-x –with-mailutils

Maybe someone more experienced with Emacs Lisp can look into it further if it is a reproducible problem and clear up why the obvious settings do not work, but other functions works. This could also be user error since I am still learning Emacs Lisp and Emacs in general. I know some things, but there are a lot more things yet for me to learn. Are my settings plain wrong? Have I misunderstood something here?

Tags: emacs computers
14 juli 2026

The Genius of Lisp

I have just read the book The Genius of Lisp by Cees de Groot. It was a very interesting read. The book tells the history of the Lisp programming language from 1958 to today and also how Lisp, as one of the earliest high level programming languages, has influenced other programming languages over the years. It also goes into the technical details of how Lisp dialects were implemented and the differences between them. And it explains the mathematical groundwork by Alonso Church and Alan Turing for the Lambda calculus, and how John McCarthy used it to build his language.

The technical details are well explained and as easy to follow as they possibly might be. There is example code in IBM 704 Assembly with explanations, practical implementation examples in JavaScript that is easy for present-day programmers to follow, explanations in pseudocode and of course lots of code examples in various Lisp dialects. The early chapters on the mathematical underpinnings of lambda calculus and the foundational work for Lisp and its practical implementation gave me a proper understanding of it even if my mathematical knowledge is very limited.

For me personally, I felt that after reading the book, I now have a better understanding of the different dialects of Lisp, who created them and for what purpose. I knew about some of the dialects and some of their traits, but I learned a lot more about them. For instance, I did not know that Common Lisp is a thoroughly object oriented language and this surprised me.

Although I knew about the previous AI hype with "expert systems" and the AI winter that coincided with the economic downturn in the late 1980s, the book enlightened me with more details and more about its consequences for the AI language Lisp. We are fast heading into a new AI winter now since the LLM providers have unsustainable business models, but I think the consequences for Python will be less dire since its growth in popularity started long before the present LLM craze and it is less closely tied to AI research than Lisp was. (I bet on Python when I studied at the only Norwegian University or College that taught it instead of Java as their first language and since then, its popularity has only grown, from at that time being less used than Java and C#, but now being the second most used language after JavaScript/TypeScript according to Stack Overflow Survey.)

Reading The Genius of Lisp has strengthened my belief in Lisp as a brilliant programming language worth exploring further. It is not on the top 20 list of programming languages now, but it is powerful and elegant with its homoiconic s-expressions for both code and data. Learning more about the different dialects through the book also further convinced me of my previously somewhat uninformed hunch that Clojure and Scheme are Lisps I should look into further. (I am already a Guix user thanks to David Wilson's brilliant SystemCrafter videos, but I haven't had the time to delve deeper into Scheme.) My first meeting with Lisp was in Emacs, and I will continue to play with Emacs Lisp as well.

All in all, it was a good read and I highly recommend this book to people interested in programming, computer history and the mathematical underpinnings of functional programming, the Lambda calculus. Some previous knowledge of JavaScript is useful for understanding the implementation of Lisp functions in the early chapters.

Tags: lisp, maclisp, interlisp, scheme, racket, emacs, elisp, common_lisp, clojure
23 juni 2026

Dunk Dunk

Ro! 🇳🇴

Tags: football
10 mai 2026

Speed improvement hack for dired with EWW

I have used EWW in Emacs for some of my browsing for a while and it does the job very well unless browsing single page applications or other sites that do not send HTML unless you run JavaScript. Thank you Lars Magne Ingebrigtsen for making EWW! Hitting R filters out all the noise for a really pleasant reading experience. It is good to browse within Emacs with no context switching in a keyboard-driven way.

(Apropos the joys of context switching: When I am a bit tired, I use Emacs keybindings everywhere. Tap-to-click is naturally off on my laptops to prevent insanity. I am often able to "help" my students a lot within just a few seconds of random keybindings and clicking everywhere on their laptops. It is really good for students to learn patience and tolerance towards people unable to handle computers. They need that for their user support class and future work in the IT industry.)

When Joshua Blaise wrote about his eww setup and use, I read it with interest and stole most of his ideas for my own config. The important one for this little hack is that I set up URLs with endings like .mp3, .mp4, .m4v, .mkv etc to launch in mpv when browsing to them. This makes watching videos and listening to audio content easy with EWW. EWW is also good for browsing local files by hitting W with point on a file in dired.

In dired, I usually launch external programs for photo editing, media playback etc by pressing & with point on a file name. This brings up the completing-read interface in the minibuffer which asks me if I would like to use dired's guess as to which program to use (which I have set to guess xdg-open first) or something else. I press return and then mpv, gimp, darktable or whatever launches with the file.

I had an html file in my downloads folder which I launched with W to read in EWW. I then, by mistake, hit W when on a .mp3 file, and it launched in mpv. Since W launches the file in EWW and EWW was configured to open .mp3 files in mpv, it did that without asking me which program to use. It is faster to hit W than to hit & RET, or in worst case write a program name and RET. So now I launch media files with W in dired in stead of using &. It speeds things up a bit.

(While writing this, I also remembered that I have functions for playing enclosure links and links in elfeed through mpv that I might replace or improve by shuffling the links to EWW or einar-browse-url-mpv. I wrote my configuration for elfeed very early in my Emacs journey, without really understanding any of it, by copying snippets from the Emacs wiki, blogs and Reddit so it is high time to look at it again anyway. I can probably simplify it.)

Here are the relevant parts of my configuration for EWW to get this working:

(defun einar-browse-url-mpv (url &rest _args)
  "Opens URL in mpv."
  (start-process "mpv" nil "mpv" url))

(use-package eww
  :config
  (setopt browse-url-handlers
          '(("\\(youtube\\.com\\|youtu\\.be\\|vimeo\\.com\\|twitch\\.tv\\)" . einar-browse-url-mpv)
            ("\\.mp3$" . einar-browse-url-mpv)
            ("\\.mp4$" . einar-browse-url-mpv)
            ("\\.webp$" . einar-browse-url-mpv)
            ("\\.m4v$" . einar-browse-url-mpv)
            ("\\.mkv$" . einar-browse-url-mpv)
            ("\\.pdf$" . einar-browse-url-pdf)
            ("." . eww-browse-url))))
Tags: emacs gnulinux
28 april 2026

Fix Emacs python-mode REPL and org code block with python evaluation problems

For a while now, the python shell in Python mode in Emacs has not worked properly on my work laptop. I get some strange glyphs in the top of the python shell buffer when opening it from a file with C-c C-p and when I tried importing a file with C-c C-c, I would get an error saying stringp was nil and every character in what I tried to import was written first once, then together with the next, then the next three and so on. It looked somewhat psychedelic and was more or less unusable.

I also had trouble evaluating python code blocks in org mode which has been annoying when presenting with inter-present-mode for my students or exporting from org files I used for presentations to PDF or docx to upload to our school's learning platform (it's learning). In org, I would get the same error saying stringp was nil when evaluating code blocks that I would get when trying to import files to the REPL in python mode.

I first thought the problem was related to my einar-python-virtualenv function which locally changes the python-shell-interpreter for a file if it is in a project with a virtual environment since I made that quite recently, but the strange thing was that it worked perfectly on GNU Guix on my main machine at home. I then thought maybe it was a Windows 11 problem since I only experienced it at work, so I tried on a Raspberry Pi 4 at work with the latest Raspberry Pi OS and I had the same problem.

I then tried launching Emacs with -q to see if the problem was related to my config or not. When I then launched the python shell from a file with C-c C-p, the REPL itself worked slightly better, but I still got some strange glyphs at the top. I then spent some time trying to figure out where in my config the problem appeared and removed first the einar-python-virtualenv and then my configuration for python-mode, but the problem persisted. I also looked into whether some setting for comint-mode might cause the problem.

After spending one and a half hour trying to troubleshoot this on Monday, I today realised I updated Python to 3.14 on my work Windows 11 laptop a while ago. I checked the python version on the Raspberry Pi and it was 3.13. I checked packages.guix.gnu.org and Guix uses version 3.11. I thought maybe something changed in one of the newer versions. I had a look at news for version 3.14 and did not find anything promising. I then checked out what was new in 3.13 and one of the first things was a new and improved python shell.

Luckily, you can get the old shell that python mode in Emacs works with by setting the environment variable PYTHON_BASIC_REPL to something. When I added this function call to my Emacs config for python mode, the python REPL works in python mode again and it is possible to evaluate org code blocks and export files that will evaluate them on export again:

(setenv "PYTHON_BASIC_REPL" "1")

I write this so others experiencing the same problem can find a solution. In the long term, python-mode, python-ts-mode and org mode needs to work with the new and improved python shell since the old shell will probably be removed at some point in the future, but for now, just setting the environment variable is enough to get both the REPL and org code blocks working again.

Tags: emacs computers windows gnulinux
Other posts
© Einar Mostad 2010 - 2026. Content is licensed under the terms of CC BY SA except code which is GNU GPL v3 or later.
Made with GNU Emacs, Org-Static-Blog, and Codeberg Pages on GNU/Linux.