Newest posts:
Emacs mistakes and misconceptions
I started using Emacs in the spring of 2022. I had been curious about it for a while and watched some videos by Protesilaos Stavrou and David Wilson that made it seem wroth trying. I thought I could try it out for a while to give it a chance and if it did not deliver, I could go back to Vim. At the time, I had been a Vim user for a couple of years and had simplified and improved the efficiency in my computing by using more CLI and TUI programs like NewsBoat (instead of gPodder and Liferea) and Calcurse (instead of Thunderbird Calendar) that I had then configured to use Vim keybindings. I felt that Vim's modal editing, even though I had become quite efficient, was a bit cumbersome even though people on YouTube boasted about how intuitive it felt to them (after "just" years of training their muscle memory), and I had trouble remembering some keybindings in Vim until I put a poster on the wall beneath my external screen with a keyboard and what every key did. I installed Emacs before a trip to Fredrikstad (maybe in the winter vacation or Easter vacation?) and on the ferry between Moss and Horten on my way back, I started working through the Emacs tutorial. Two years earlier, I did the same on the same ferry with the Vim tutor, but unlike the Emacs tutorial, I had to do the Vim tutor many times for the contents to really stick.
Even though I was exposed to the idea of Emacs as a cohesive environment before trying it through videos by Prot and David Wilson, I think a misconception I had early on was that it was a text editor like Vim, but with a bit more extensibility. It is not completely wrong because it is the better of the two/three best text editors in my present opinion, and that joke about it being an OS lacking only a decent text editor is obviously a joke for anyone that has used it for enough time to get to know it. For instance, I soon discovered that Emacs default keybindings made me faster than modal Vim keybindings since there are one or two key presses less for every edit. You don't have to press Esc to get into Normal Mode and you don't have to press i, o, a… to get into Insert Mode in addition to pressing keys for moving around. I had heard tales of awful key chords, but in reality, text editing used a modifier like Ctrl or Alt plus a key to move around, and I could just write to write. It felt a lot more natural and intuitive than Vim to me, even after using Vim for two years. Keybindings were also easier to remember in Emacs since they were mnemonic. (F for forward, b for backward, n for next, p for previous, d for delete, y for yank — and w for kill? Some years later, Mike Zamansky cleared that up in a video where he mentioned w was for whack, which made me laugh.)
Without the experience of a more cohesive workflow within Emacs, it is hard to imagine how useful an Elisp interpreter built around text editing can really be. Not having to bind keys to get your TUI programs to have the same keybindings as your text editor is one efficiency advantage of doing most things inside Emacs over (Neo)Vim + CLI and TUI programs, but the ability to extend or change functionality for anything you do with Elisp through well-documented functions and variables is the real superpower of doing most things within Emacs. People can tell you that, but to understand it, you have to experience it. It is not just a text editor with a scripting language for extensions, it is a programming language environment built around text editing. The use of a Lisp is also a great advantage since it is simple, easy and fast to learn and more efficient to use than other programming languages.
I did not have much time to explore Emacs in the beginning since I taught English while also studying IT, so in the start, I replaced Vim with Emacs as a text editor and kept using the rest of my CLI, TUI and GUI programs as I did before. I configured Emacs with settings from the Emacs from scratch video series by David Wilson. That worked well, and it improved my text editing speed, but I missed out on one of the main advantages of Emacs which is the cohesive work environment without context switching that Prot and David Wilson talked about until I found more time later to look into what else Emacs could do for me. I sort of knew all along that there was more to gain, and I did start to use Elfeed quite early on as a replacement for Newsboat, but otherwise, I kept using Emacs mainly as a text editor. There is nothing wrong with that, but to really gain the advantages of the cohesive work flow you can get by using Emacs, integrating other stuff into Emacs makes sense. Later on when I had more time, I did slowly replace one program at a time with Emacs modes or Emacs packages.
I think one of my main mistakes when learning Emacs was to rely too much on videos, blog articles and searches online instead of reading up more on the built-in documentation. The info reader felt a bit foreign with its somewhat strange keybindings and even though I realised C-h was there with a lot of other functionality as well, I did not really start using C-h k to find out what keybindings did, C-h m to find out what I could do in a mode, C-h v for looking up what a variable is or C-h f to look up functions, until a couple of years later. This was also related to not having much time to invest into learning Emacs in the start since I had started teaching at the vocational IT and Media production study line in the autumn of 2023 while still studying IT which made me extremely busy with making teaching materials since there exists no books for any of my subjects and at the time not much material was available on NDLA, a Norwegian digital learning portal used a lot in upper secondary (= US high) schools. If I had spent less time reading blogs and watching videos, but instead read up on the manuals and started using the built-in documentation more, early on, I think I might have come further faster. On the other hand, the great thing with blog posts and videos is that you get inspired by what and how other people use Emacs. Since configuring Vim is somewhat limited compared to programming Emacs, I was used to find most of the information I needed online and just occasionally dip into :help in Vim. Both the built-in documentation and online content are valuable, but I mainly relied on online content in the start which was a mistake.
Use python shell from virtual environment if there is one in Emacs
About a week ago, I made a function to use python from a virtual environment if there existed a directory called venv within the project the file is inside. The point is to get access to the packages within that virtual environment when using the python shell to evaluate code from a file. Today, I had a look at that function again. I thought it would be really nice if I could find the python executable within a virtual environment no matter what the virtual environment directory is called.
I looked around a bit and found a built in function in Emacs to search based on a regular expression within a directory recursively (within subdirectories) that solved the problem. I am not very experienced with Emacs Lisp, so this is a function I haven't met before.
(defun emo-python-virtualenv () "Sets the python shell to python from a virtual environment if one exists." (when (project-current) (let ((pythonpath (nth 0 (directory-files-recursively (nth 2 (project-current)) (if (eq system-type 'gnu/linux) "python$" "python.exe$"))))) (when (file-exists-p pythonpath) (setq-local python-shell-interpreter pythonpath)))))
As mentioned last time I wrote about this, I also need to run this function whenever I open a python file to set the correct path to the python shell for that file. This is done by adding the function to the python-mode hook like this:
(add-hook 'python-mode-hook 'emo-python-virtualenv))
With this in place, whenever I open a python shell with C-c C-p from a python file, I get a python shell from within the virtual environment of that file's project, or I get the system python if there isn't a virtual environment within the project the file is part of.
Use virtual environment in Emacs' Python Mode if in a project with a venv
Today, I was going to work on a python project for half an hour at the end of my work day. I tried to find a good python module to use to work with RSS and Atom feeds. I found one and installed it in my virtual environment and pasted in some example code in a buffer in Emacs. I wanted to run the code in the python shell with Emacs' handy C-c C-c keyboard shortcut. So I opened the shell with C-c C-p and then hit C-c C-c, but I got an error that the shell did not understand the import on line 1 where I was importing the module I just installed in my virtualenv. I realised the shell ran from outside the virtualenv. I had not looked into this before, but found there were various packages people recommended to fix this. I tried one that looked interesting, but it did not work. Maybe because I was on my work laptop with Windows at the time. I tried a few things, but soon gave up.
I then discovered .dir-locals.el files was a possible solution. It gives buffer-local values to variables inside the folder it is placed for the mode that you configure through an alist. The first suggestion I found was to use a variable that did not work at all. I wasted a lot of time trying to get it to work. When I was close to giving up, I discovered that I could use the python-shell-interpreter variable in the .dir-locals.el file and it worked. If you launch the python shell from the python within the virtual environment, then the shell also sees all the other packages installed in that virtual environment.
I then thought that the solution I had found would not work on my own machines that run GNU/Linux since the python.exe-file neither exists nor the folder it is in in virtual environments on my preferred platform. A .dir-locals.el-file inside a project checked in with git is not a particularly cross-platform solution to this problem for this reason and it would be a hassle to make one for every python project with a virtual environment. I thought I could make a function in my Emacs configuration that would check which platform I was on, whether I was in a project or not and whether a directory with the name venv exists (I tend to use that name for virtual environments) and then just set the variable to the correct path based on that.
When I got home, after dinner, I made that function. I love how well-documented Emacs is. It makes doing things like this really easy. I just looked up function names starting with project and found something useful after reading up on a few. A bit of evaluation in different buffers with C-x C-e to check if things worked the way I thought and a bit of tweaking when it turned out that cddr did not return the same as nth 2 (cddr returned a list with a string, but nth 2 returned a string) and then I ended up with this function:
(defun emo-python-virtualenv () "Sets the python interpreter to python in the venv if in a project and a venv exists." (when (project-current) (let ((pythonpath (concat (nth 2 (project-current)) (if (eq system-type 'gnu/linux) "venv/bin/python" "venv/Scripts/python.exe")))) (when (file-exists-p pythonpath) (setq-local python-shell-interpreter pythonpath)))))
To put it to work every time I open a python file, I also needed to add it to python-mode-hook like this in my configuration for Python mode:
(add-hook 'python-mode-hook 'emo-python-virtualenv)
When I now try to import packages that only exists in a virtual environment into the python shell, it works if I loaded the shell from a file within the same project as the virtual environment, but not from a file from outside that project. If I load the shell from a file within a project without a venv, I get the system's Python and its available packages. No need for outdated packages that doesn't work or directory-local variables that are platform dependent. This works on Windows as well. It's easy and fun to fix things like this in Emacs, and I learned some Elisp and a bit about Python virtual environments in the process. It would be convenient if things like these worked out of the box, but that would demand a bit more code since my function works on the basis of the directory name venv for every virtual environment. If I can find a way to do this that is not dependent on the name of the virtual environment folder, that would be even better.
Fix error with Cider Clojure REPL in Emacs on Guix
I am currently reading Clojure for the brave and true by Daniel Higginbotham and in Chapter 2 about how to use Emacs for Clojure, there are instructions on how to set up Emacs with Cider, a Clojure REPL. I tried installing the package with a use-package configuration with :ensure t and I got the package. However, when I tried using it as the book instructed, I just got an error. I then realised the need for OpenJDK and Clojure and installed those with my home-config.scm file in Guix. I tried again and I still got the same error. I then tried installing emacs-cider from Guix as well with the hope that maybe there was some kind of incompatibility between OpenJDK, Clojure and Cider that was ironed out in the guix package. It still did not work.
I tried looking up the error and found a blog post that suggested installing OpenJDK non-Guixily by downloading a binary and adding it to the $PATH which solved the problem for that user. I did not really want this, since I really like the idea of installing my whole system with a scheme file or two in a reproducible way. (I'm not there yet, but I hope to get there over time. I just started using Guix as my main distro a few weeks ago. For now, after installing Guix System or Guix on another distro, I do some manual work and then run a Shell script to set up a few folders and run the command that applies my home-config.)
Tonight, a day later, I DuckDuckWent again and found another blog post about Clojure that told me that since there are three outputs for the openjdk package, to get everything I would need for Clojure, I should install openjdk:jdk, not simply openjdk which would give me openjdk:out. In addition, I also added openjdk:doc since I am a fan of having as much documentation as possible available on my local machine. I saw a video where that documentation was read from inside Cider a couple of days ago, so I thought it might come in handy. Come to think of it, maybe that was accessed over the internet, not from the local machine. Anyway, it doesn't hurt to have some documentation.
And now, everything just works. Turns out the problem was just user error. Whenever there are multiple outputs of a package in Guix, it is important to know what you get with each output and choose accordingly. As someone who just recently switched my main machine to Guix, this is very useful information that I had not seen before. I thought I would write this up in a blog post so people out there trying to fix the same problem have a chance of finding a solution faster than I did, and for my own future reference.
Adventures in Guix
I have been interested in the Guix functional package manager and GNU distribution for a long time. It has some brilliant features like being able to roll-back to a previous generation of your system if an update fails, easy spinning up of development containers where you can functionally and reproducibly install the dependencies you need for your project and you can configure your whole system with Guile Scheme (a dialect of Lisp). I have tried Guix from time to time with varying results. Since I have been busy the last few years learning technologies I need at work, haven't had the time to learn Guix as well until now. However, I have found some time and energy lately to play with Guix and when I did, things seemed to work a lot better than my previous attempts. Spending the time to read up on the documentation helped, in addition to the many contributions by the Guix community to improve the distro / package manager since my previous attempts.
I have just started to use Guix on my main machine. As with anything new and different, there has been some hiccups along the way. For example, hunspell-nb which is the spell checker I use for Norwegian Bokmål isn't packaged for Guix. However, aspell-nb is and the only reason I switched from aspell to hunspell was that I also use Emacs on Windows at work (native Windows Emacs since WSLg doesn't work properly and I need GUI Emacs) and native aspell-nb is hard to get on Windows, but Hunspell-nb can be used if you install LibreOffice and copy some files after installing hunspell from chocolatey (where of course hunspell-nb is not packaged either). I prefer not having if-statements in my Emacs config if I can avoid it, so I switched to hunspell on GNU/Linux as well, but since it is not pacakged in Guix, I reverted the config back to when I used aspell on GNU/Linux and Hunspell on Windows.
However, that did not work. At every startup, Emacs complained that it could not read the nb dictionary from the address to the aspell-package in my current home-config. I tried everything, but it just didn't work. I then tried M-x ispell-change-dictionary and hit tab to have a look at the suggested dictionaries. Turns out that aspell-nb on Guix supplies a dictionary it calls no, unlike on every other distro, where it is called nb. This makes absolutely no sense since there are two Norwegian written languages, Norwegian Bokmål (nb) and Norwegian Nynorsk (nn), but no written language called just Norwegian (no). Packages that supply both tend to be called no, like hunspell-no which supplies nb_NO and nn_NO. It is easy to change my config to use the no dictionary and my emo-ispell-toggle function to toggle between no and british on GNU/Linux. I had a look at the package definition for aspell-nb, but it seemed to just supply the upstream package as far as I could tell as a very inexperienced Guix-user.
Another strange thing is that mpc mode in Emacs does not want to speak with mpd. It also took me a while to understand that mpd was packaged for Guix since I looked for mpd, but the package is called mpd-mpc in Guix. The strange thing is that my own emo-play-album function in Emacs that uses mpc shell commands work fine from the same Emacs that give me an error that it could not communicate with mpd when I try to show the songs in an album in mpc mode. I use the same mpd config file as on other distros where it does work. I tried changing some settings in the config file to see if I was able to fix it, but no luck thus far.
I also miss the frogr package for easy uploading of photos and adding of metadata to Flickr. When I get a bit more experienced, maybe I can contribute a package definition for it?
Another thing I have noticed is that Guix calls my locale in the LANG environment variable nb_NO.utf8, not nb_NO.UTF-8 like every other distro. I wonder if this might break localisation in programs that use the environment variable $LANG to choose which localisation to use, but I don't really know how this works so this might not be a problem at all. It seems strange and random, but maybe there are good reasons for it that I don't know.
Except for these new user problems, the experience is nice. After some tweaking and trial and error, I now have a very simple and basic system config that just installs the absolute minimum I need to have at the system level, and then I use Guix home to deploy my user packages and services. I have some Bash scripts in my installscripts repository as well as my Scheme config files that I can use to set up a system, either on Guix or on a "foreign distro" after an install with just basic system tools. Ideally, I would do that with Scheme in the system config, but I don't know Guix and Scheme well enough to do that yet. I'll get there over time. Using Guix as a package manager on top of another distro can be useful if I need to use Secureboot which Guix does not support, for example for a dual boot with Windows 11 which demands Secureboot. I really like that Guix adheres to the Free Software Foundation's Free System Distribution Guidelines, but when used on hardware I have not chosen myself, it can be useful to have another distro underneath that supplies non-free drivers for that hardware as well as support for Secureboot. I am slowly climbing up the freedom ladder in my personal computing and at every step, there is less lock-in, privacy invasion and enshittification, and more freedom and empowerment.