This is a holistic introduction to Emacs: how useful it is and how it champions free software. It is a modified version of the talk I did for the “FLOSS @ Oxford” event, organised by people at the University of Oxford. This is the page I wrote about that event: https://protesilaos.com/codelog/2026-03-12-my-emacs-talk-floss-oxford/.
Hello everyone! My name is Protesilaos, also known as “Prot”.
This presentation is a modified version of the talk I gave last night at the FLOSS @ Oxford event: https://ox.ogeer.org/event/computing-in-freedom-with-gnu-emacs-protesilaos-stavrou.
It was an event organised by people from the University of Oxford. I thank them for giving me the opportunity to participate in their programme.
I want to have this modified version here for people who do not read my website. They may not be aware that I talked at this Oxford event.
Having the video on this platform means that everyone can benefit from it.
In this presentation I will talk to you about GNU Emacs, or simply, “Emacs”. Emacs is a program you run on your computer. I am using it right now for this presentation.
Emacs is free or libre software. It allows you to read all of its source code, to modify it, and to share it with your customisations. Thus you contribute to—and benefit from—a community of welcoming Emacs users.
I will tell you what all this means in practice and how you can improve your computing experience by switching to Emacs.
When you first start using Emacs, it feels like a regular text editor program.
You move the cursor around and edit text. Nothing obviously impressive out-of-the-box.
As a text editor, Emacs is highly capable. It has all sorts of keyboard shortcuts that let you efficiently operate on text.
You can control Emacs without relying on the mouse, if you want.
Emacs supports the Unicode standard, which is essential for inclusivity of peoples.
The world’s scripts can be expressed in Emacs. I am a native Greek speaker.
I can use functionality that is built into Emacs to switch to the Greek alphabet in order to write something, such as to say «καλησπέρα», which means “good evening”.
I can even spell out “Dao De Jing” (道德经), which is the title of a book from ancient China.
Plus emoji: 🦚🦬🐉.
The multitude of scripts can be present in the same document.
This is an advantage for multilingual people like myself or those who do research that involves many natural languages.
Emacs can combine several fonts in the same page as well as different colours.
Each fonts can have its own attributes, such as for its relative size and typographic intensity.
Same idea for colours.
On my screen right now, I am already combining two different font styles: that of the heading and the body of the text.
Emacs does not limit you to a text-only interface. It can also display images and PDF documents. Below I have a link to an image file. I will now type a keyboard shortcut to reveal this image. And I will do it again to hide it.
This, by the way, is a spot somewhere in my mountains.
Although you can benefit from using Emacs as a generic text editor, what really appeals to people like me is the option to extend Emacs.
“Extend” here means to introduce new functionality; functionality that is not available in the default program you install on your computer.
These extensions are written in the same programming language as most of Emacs. It is a programming language called “Emacs Lisp” or “Elisp”.
You can extend Emacs on your own, by writing some program in Elisp, or you can download an existing extension that the community has made available.
For example, when I create a new extension for Emacs, I publish it under the terms of a free software license—the same terms that Emacs uses.
Others can then download my extension and use it as they prefer. If they want, they can make their own modifications on top, which may introduce other extensions that I had not thought of in my original implementation.
And if those users follow my example, then I can also benefit from their additions once they publish them.
As such, there exists a community of enthusiastic users of Emacs who care about sharing their works with the rest of the world.
Users can extend Emacs by running some Emacs Lisp program. Such a program can be as short as a single line. Or it can be as long as it needs to be. It does not matter.
Users run the program and Emacs immediately does what the program renders possible.
For example, I am doing this presentation inside of Emacs. But Emacs does not have a “presentation mode” built into it. I thus developed my own extension which empowers me to do what I am doing right now.
Let me toggle off my presentation mode to show you what I mean.
Notice that the display has changed.My main font is monospaced now.
The headings are smaller than they were before: they are the same size as the rest of the text. There is no number next to the heading anymore.
Then, there is a bar at the bottom of my screen, with information about what I am working on. On the side, there are line numbers, indicating where my cursor is in this file. Plus, my current line is highlighted with a distinct background colour. Let me shift it up and down to illustrate this point.
All those elements are useful while I am programming. But they look distracting when I wish to focus on some portion of text. So, I just type the keyboard shortcut I have and—voilà!—I get the style I prefer.
You may wonder: why do I even need a customisable text editor?
The answer is about control. You are in charge of what you use and how you use it. You can piece together a workflow that works the way you prefer.
This presentation mode I toggled on and off earlier behaves exactly how I want. I decided which set of interface tweaks to apply. Another user may have a different preference in this regard.
For instance, they may like having line numbers on the side of the screen. There is no right or wrong answer. What matters is that Emacs gives us the means to do what makes sense to us.
Now apply this principle to everything you can use Emacs for: this will generally be a text-centric project.
I run my agenda exclusively through Emacs.
I handle all my email correspondence with Emacs.
I do programming and I write prose, such as blog posts for my website and books or technical manuals.
For each of these, I know that Emacs will empower me to perform my tasks without arbitrary restrictions.
Emacs lets me use Elisp to modify how I do my emails, for instance, and how I present tasks in my custom agenda view.
Without Emacs, I would not be in a position to control my computing experience to the extent I do.
The reason is that I would be relying on many different applications. Each application has its own interface and design paradigms.
Each application is configured, if at all, in a way that is specific to it. Customisations in one application do not carry over to other applications.
And, if we consider the important implementation details, each application may configurable in its own programming language.
In other words, that is not an integrated computing experience.
To have the same degree of control that Emacs makes possible, I would have to hope that somehow all those disparate applications would conspire in my favour.
That is wishful thinking.
The reality is that piecing together many different applications is an exercise in frustration and the path to a life of ever-distracting context switching.
Having everything I need inside of Emacs ensures that things happen in a manner that is consistent.
All customisations are written in the same programming language, namely, Emacs Lisp.
What I define for one context, such as this “presentation mode”, can be used in another context.
For example, I can have this presentation style enabled when I read emails. Why?
Because it can make it more comfortable for me at a certain hour. And I can even automate this with conditional logic, so it happens on its own when I open a new email under certain circumstances.
When you work with many applications that do not play nicely together, you cannot do something that the developers have not envisaged.
For example, your email client likely does not have access to a “presentation mode”. Same for your other applications.
Similarly, your many applications will not necessarily know how to read and interpret the configurations you have in one application.
Suppose you define your favourite colour scheme for your email client. You take the time to consider the harmonies and use precise typography to your liking.
Now, you switch to your calendar application and none of that work carries over: you have to do it again, assuming it is even possible.
Colours and styles may seem like relatively small issues. But they are indicative of something greater: disparate applications do not work together seamlessly.
Emacs does not have this problem. You define something for one context you have in mind and, eventually, it can be used in another context that initially you had not even thought of.
For example, in my Emacs I wrote a small function to quickly copy the “thing” at where the cursor is. This is useful when I do programming, as the “thing” can be an entire expression, like the definition of a function. But the “thing” may also be a link that I got in my email.
I had not thought of that use-case in advance. Yet it was trivial to have my function do what I need in this once unforeseen situation.
The integrated computing environment of Emacs is more than the sum of its parts.
This is because you can combine different pieces of functionality in ways that the original developer had not foresaw.
You do not simply have your writing, your email, your agenda, et cetera, in Emacs.
You have the functionality of one in tandem with the functionality of another. And you draw linkages between them as you see fit.
Consider once again this presentation I am now doing. What I have in front of me is the transcript of my talk. This is a plain text document, which I can edit live. Let me CAPITALISE THIS to illustrate the point.
I have made this file look a little bit like a series of slides.
Notice that if I scroll up and down, which I will do shortly, you only get the current section I am reading from: you do not have access to the rest of the document. I will scroll up and down now.
This is a feature known as “narrowing”. Let me “widen” the view and then try to scroll again. You will now be exposed to the rest of the text.
The original developer of this “narrowing” facility did not know how someone like me would make use of it.
I have it here for my presentation. Each heading becomes its own pseudo-slide. I have narrowing for my emails, when I want to read a portion of the text in a more focused way. It is all about how I choose to do my computing.
For many years before switching to Emacs, I did not enjoy using the computer.
I needed too much time to accomplish every single task.
I could never find any of my files in a timely fashion because there was no program that would enforce on my behalf a predictable file-naming scheme.
All my notes were eventually not retrievable. This made them useless. Data you save is only good if you can find what you are searching for.
My music collection was inconsistent because I needed special software to write the metadata… In short, I was not as productive as I would like to be.
And, above all, it was not fun.
Most of my work at the time was centred around the email client and a word processor.
The email client had its own subsystem for handling reminders for tasks. The format of those tasks was not interoperable with other programs.
I could not access the tasks with my favourite text editor. I thus had to use the clunky interface of the email client, which was never designed for task management—and was not configurable.
And then I had all the cognitively burdensome annoyances of my two applications looking quite different from each other.
My emails did not behave like my documents, which made it harder for me to flip between the two and continue writing. I would roll my eyes each time.
Emacs has elevated my computing experience.
I have been much more productive ever since I switched to it. Allow me to demonstrate a tiny bit of what I do each day.
I will temporarily exit the presentation mode in this window.
Then, in the bottom half of my screen, I will open my email client to read a message I got.
Once you follow my switch to the email client, I will hide the window that shows this presentation.
After that I will switch to my agenda to record a task and review what I have to do.
All this is done inside of Emacs. Time for action!
What I just demonstrated is a very small part of what I do every single day.
There is much more, though I cannot cover it all in this presentation.
The point, however, is the consistency of the experience; consistency throughout.
I have customised my email client by writing some Emacs Lisp code for it. I have done the same for the custom agenda I have. And much more.
Every time I work with Emacs Lisp, I acquire skills that are applicable outside the confines of the problem I am solving.
For example, by configuring email the way I want, I pick up programming skills that I can then apply to the design of my custom agenda.
This is an investment that pays off more and more.
Emacs will adapt to match my evolving needs. Each new workflow I incorporate in my Emacs setup will thus benefit from all the knowledge and features I have accumulated.
I do not have to relearn everything because I am not switching to another application.
I do not have to throw away all the work I did all those years. It is here to stay.
I do not feel the pressure to try the new shiny app of the day. I did that many times and always regretted it. I lost my data and time in the process.
Because I am rooted in this stability of Emacs, I remain productive and efficient.
I mentioned earlier that Emacs is free or libre software. This means that you can read its source code, modify it, and share your changes with others.
Emacs has a license that gives users power. There is no corporation that can take Emacs away from us. It belongs to the community and we all tend to its wellness.
In the case of Emacs, software freedom is not just about the license. It informs how you use the program. Emacs makes such freedom an irriducible part of its functionality.
You can, at any moment, ask Emacs what does a keyboard shortcut actually do. What is the definition of a function. What is the value of a variable. And you may even access the source code to check for yourself.
I will demonstrate this right now.
I actually learnt to program in Emacs Lisp by exercising this freedom.
I would tinker with Emacs and continuously check on its state. What does this do? Which function is called by that keyboard shortcut? How is a program able to determine if the file is not saved?
I wanted to learn how, for example, we move down a line. From there, I learnt that we can move down many lines at once.
I then figured that we can move down the lines and then also do something else, such as place the cursor at the end of the line and create a pulse effect to bring attention to it.
Not only did I learn how to configure Emacs, I even wrote tens of extensions for it. I have also authored a libre book titled “Emacs Lisp Elements”. This freedom is not theoretical. I did not have a background in programming, yet was empowered to act and to grow as a person.
Emacs is extended with Emacs Lisp. If you know how to program in that language, you can be extra opinionated and particular about the way Emacs facilitates your work.
But even without any expertise of this sort, you can still do much of what you like. Remember that I started using Emacs without a background in programming.
Emacs blurs the distinction between user and developer. Many of the developers actually start out as users like myself. They learn along the way and, eventually, they contribute to the development of Emacs.
I even have written code that is in core Emacs: my modus-themes as
well as several other smaller patches.
The Emacs community has developed a rich corpus of extensions. You do not need to invent anything right away in order to be productive.
We call these extensions “packages”, as they are distributed in a way that makes them easy to install and then use directly.
The Emacs program you will download on your computer ships with plenty of packages built-in.
Depending on your needs, you may not even have to install anything from what the community has to offer.
Though if you want a package, it is fairly easy to get it and run it on your system.
Emacs is not picky about how you should use it. You are empowered to be opinionated.
For example, Emacs ships with a package called org or “Org mode”. At
its core, this is a markup language. I am using it right now in this
document.
Notice how lines that start with an asterisk function as headings. This is what the markup does.
Org lets you write documents, including books, handle your tasks, organise your agenda, and much more. It is a powerhouse.
There are so many things to discover in Emacs as well as the broader package ecosystem.
Emacs as a whole provides high quality documentation that explains everything.
When you install Emacs, you get with it plenty of technical manuals. There is also an interactive tutorial to help you make sense of the basics.
Furthermore, when you ask Emacs for help about the definition of a function or the value of a variable, you receive the documentation for the thing you are looking for.
The expectation for all contributions to the official Emacs program is that the code is well-documented and the manual is updated accordingly.
Core Emacs sets the standard of what good documentation looks like. Package developers follow this practice.
For example, my denote package has a manual that is over 7500 lines
long. It exceeds 52000 words. In it users find detailed instructions
as well as code snippets that they can copy and use outright. And this
is not the exception. All my packages are like that, to the extent
necessary. Most other developers do the same.
As a community, we have access to so much knowledge for free and in freedom. If we are committed enough, we can learn from others and thus become better ourselves. We do so in a spirit of sharing and caring. For me, specifically, all this was of great help. I am self-taught because I received all those great resources from the community. I consider it my duty to give back in kind.
Because Emacs is extensible, there is practically no limit to what you can do with it. At least this is the case for all tasks that are text-heavy.
Emacs will just gracefully evolve to match your requirements, provided you can extend it on your own or with a relevant package.
The downside, however, is that it is not easy to become proficient in it. If you are committed, you can learn the basics within the first few days.
Though you will need to invest a few weeks or months to become skillful. It depends on how much effort you put into it, what sort of work you are doing, and what your background is.
I learnt the basics within a few days. I started writing my own Emacs
Lisp within weeks. And within a year I had my modus-themes moved
into core Emacs.
Several “starter kits” are available to help you get started. They set things up so that you do not need to discover everything at the outset.
The new version of Emacs (Emacs 31) will even come with a “newcomers theme”, which configures several settings in advance.
These can make the learning curve a bit smoother. For me, anything that improves the onboarding experiences is a plus.
Though I do not think that Emacs will ever become “plug and play”. This is due to its sheer depth and extensibility. It does so much that you still need to invest the time and effort into learning it.
However you start, the most reliable study involves the manuals. Those are written for the benefit of the user. Read them carefully.
What I can say with confidence is that Emacs is not for tourists. You cannot switch to it with the expectation that you will have a good time right away.
No. That will not work. There simply is no shortcut to excellence.
I encourage you to take it one step at a time. Emacs will make you more productive, provided you are patient enough to unlock its virtually boundless potential.
Take it slow and be methodical. Rely on the official manual no matter your starting point. Read from it and gradually incorporate its insights into your workflow.
The community—myself included—has plenty of resources to complement that study. Blog posts, video tutorials, books… But do not skip the official manual. Learning it slowly means that you will become proficient faster than you otherwise would.
I already talked about the technical side of things with regard to the integrated computing environment. Now combine that with two facts:
Emacs is not old, it is timeless. This is because it can be extended in a spirit of freedom.
Whatever new technology or idea we have as a collective, we can eventually bring it into Emacs.
This way, our integrated computing environment adapts with the times.
Thus Emacs remains ever-relevant.
Couched in those terms, the initial effort you will put into learning Emacs is actually not that much.
You have to maintain a longer-term view of this project.
If you are patient, Emacs will be one of the most reliable tools you will ever use throughout your life. And I say this as a handy man myself, someone who uses many tools for manual labour, having built the house I am in, among others.
I switched to Emacs in the summer of 2019. It is almost 7 years already. I see no reason not to use it for the next 7 years, if I can.
I will still want to write articles, do programming, maintain my agenda, and probably make presentations like this one.
Remember that you will not learn Emacs over the weekend. You are in it for the long-term. Take it slow and you will enjoy the experience.
This is all I have for you today folks. Thank you very much for your attention!
You can find this and everything else I publish on my website: https://protesilaos.com.
]]>I had written the transcript ahead of time to make my presentation more accessible. The event was held live as a Jitsi call. There were questions from participants, which I answered. A recording of the event will be available before the end of this week. I will update this article to include a link to the video.
UPDATE 2026-03-16 07:49 +0200: The video is here: https://ogeer.org/ox/rec/emacs/.
Below is the text of my talk. It is titled “Computing in freedom with GNU Emacs”. Note that some parts of my presentation only make sense in the video format, though I tried to describe in the transcript what I was demonstrating.
Hello everyone! My name is Protesilaos, also known as “Prot”. I am joining you from the mountains of Cyprus. Cyprus is an island in the Eastern Mediterranean Sea.
In this presentation I will talk to you about GNU Emacs, or simply, “Emacs”. Emacs is a program you run on your computer. I am using it right now for this presentation.
Emacs is free or libre software. It allows you to read all of its source code, to modify it, and to share it with your customisations. Thus you contribute to—and benefit from—a community of welcoming Emacs users.
I will tell you what all this means in practice and how you can improve your computing experience by switching to Emacs.
When you first start using Emacs, it feels like a regular text editor program. You move the cursor around and edit text. Nothing obviously impressive out-of-the-box. As a text editor, Emacs is highly capable. It has all sorts of keyboard shortcuts that let you efficiently operate on text. You can control Emacs without relying on the mouse, if you want.
Emacs supports the Unicode standard, which is essential for inclusivity of peoples. The world’s scripts can be expressed in Emacs. I am a native Greek speaker. I can use functionality that is built into Emacs to switch to the Greek alphabet in order to write something, such as to say «καλησπέρα», which means “good evening”. I can even spell out “Dao De Jing” (道德经), which is the title of a book from ancient China. Plus emoji: 🙃.
The multitude of scripts can be present in the same document. This is an advantage for multilingual people or those who do research that involves many natural languages.
Emacs can combine several fonts in the same page as well as different colours. Each fonts can have its own attributes, such as for its relative size and typographic intensity. Same idea for colours. On my screen right now, I am already combining two different font styles: that of the heading and the body of the text.
Emacs does not limit you to a text-only interface. It can also display images and PDF documents. Below I have a link to an image file. I will now type a keyboard shortcut to reveal this image. And I will do it again to hide it.
[Here is an image that I do not need to reproduce on my website: the specific image does not matter]
This, by the way, is a spot somewhere in my mountains.
Although you can benefit from using Emacs as a generic text editor, what really appeals to people like me is the option to extend Emacs. “Extend” here means to introduce new functionality; functionality that is not available in the default program you install on your computer.
These extensions are written in the same programming language as most of Emacs. It is a programming language called “Emacs Lisp” or “Elisp”. You can extend Emacs on your own, by writing some program in Elisp, or you can download an existing extension that the community has made available.
For example, when I create a new extension for Emacs, I publish it under the terms of a free software license—the same terms that Emacs uses. Others can then download my extension and use it as they prefer. If they want, they can make their own modifications on top, which may introduce other extensions that I had not thought of in my original implementation. And if those users follow my example, then I can also benefit from their additions once they publish them.
As such, there exists a community of enthusiastic users of Emacs who care about sharing their works with the rest of the world.
Users can extend Emacs by running some Emacs Lisp program. Such a program can be as small as a single line. Or it can be as long as it needs to be. It does not matter. Users run the program and Emacs immediately does what the program renders possible.
For example, I am doing this presentation inside of Emacs. But Emacs does not have a “presentation mode” built into it. I thus developed my own extension which empowers me to do what I am doing right now. Let me toggle off my presentation mode to show you what I mean.
Notice that the display has changed. My main font is monospaced now. The headings are smaller than they were before: they are the same size as the rest of the text. There is no number next to the heading anymore. Then, there is a bar at the bottom of my screen, with information about what I am working on. On the side, there are line numbers, indicating where my cursor is in this file. Plus, my current line is highlighted with a distinct background colour. Let me shift it up and down to illustrate this point.
All those elements are useful while I am programming. But they look distracting when I wish to focus on some portion of text. So, I just type the keyboard shortcut I have and—voilà!—I get the style I prefer.
You may wonder: why do I even need a customisable text editor? The answer is about control. You are in charge of what you use and how you use it. You can piece together a workflow that works the way you prefer.
This presentation mode I toggled on and off earlier behaves exactly how I want. I decided which set of interface tweaks to apply. Another user may have a different preference in this regard. For instance, they may like having line numbers on the side of the screen. There is no right or wrong answer. What matters is that Emacs gives us the means to do what makes sense to us.
Now apply this principle to everything you can use Emacs for: this will generally be a text-centric project. I run my agenda exclusively through Emacs. I handle all my email correspondence with Emacs. I do programming and I write prose, such as blog posts for my website and books or technical manuals.
For each of these, I know that Emacs will empower me to perform my tasks without arbitrary restrictions. Emacs lets me use Elisp to modify how I do my emails, for instance, and how I present tasks in my custom agenda view.
Without Emacs, I would not be in a position to control my computing experience to the extent I do. The reason is that I would be relying on many different applications. Each application has its own interface and design paradigms. Each application is configured, if at all, in a way that is specific to it. Customisations in one application do not carry over to other applications. And, if we consider the important implementation details, each application may configurable in its own programming language.
In other words, that is not an integrated computing experience. To have the same degree of control that Emacs makes possible, I would have to hope that somehow all those disparate applications would conspire in my favour. That is wishful thinking. The reality is that piecing together many different applications is an exercise in frustration and the path to a life of ever-distracting context switching.
Having everything I need inside of Emacs ensures that things happen in a manner that is consistent. All customisations are written in the same programming language, namely, Emacs Lisp. What I define for one context, such as this “presentation mode”, can be used in another context. For example, I can have this presentation style enabled when I read emails. Why? Because it can make it more comfortable for me at a certain hour. And I can even automate this, so it happens on its own when I open a new email.
When you work with many applications that do not play nicely together, you cannot do something that the developers have not envisaged. For example, your email client likely does not have access to a “presentation mode”. Same for your other applications.
Similarly, your many applications will not necessarily know how to read and interpret the configurations you have in one application. Suppose you define your favourite colour scheme for your email client. You take the time to consider the harmonies and use precise typography to your liking. Now, you switch to your calendar application and none of that work carries over: you have to do it again, assuming it is even possible.
Colours and styles may seem like relatively small issues. But they are indicative of something greater: disparate applications do not work together seamlessly.
Emacs does not have this problem. You define something for one context you have in mind and, eventually, it can be used in another context that initially you had not even thought of. For example, in my Emacs I wrote a small function to quickly copy the “thing” at where the cursor is. This is useful when I do programming, as the “thing” can be an entire expression, like the definition of a function. But the “thing” may also be a link that I got in my email. I had not thought of that use-case in advance.
The integrated computing environment of Emacs is more than the sum of its parts. This is because you can combine different pieces of functionality in ways that the original developer had not foresaw. You do not simply have your writing, your email, your agenda, et cetera, in Emacs. You have the functionality of one in tandem with the functionality of another. And you draw linkages between them as you see fit.
Consider once again this presentation I am now doing. What I have in front of me is the transcript of my talk. This is a plain text document, which I can edit live. Let me CAPITALISE THIS to illustrate the point. But I have made this file look a little bit like a series of slides. Notice that if I scroll up and down, which I will do now, you only get the current section I am reading from: you do not have access to the rest of the document. This is a feature known as “narrowing”. Let me “widen” the view and then try to scroll again. You will now be exposed to the rest of the text.
The original developer of this “narrowing” facility did not know how someone like me would make use of it. I have it here for my presentation. Each heading becomes its own pseudo-slide. I have narrowing for my emails, when I want to read a portion of the text in a more focused way. It is all about how I choose to do my computing.
For many years before switching to Emacs, I did not enjoy using the computer. I needed too much time to accomplish every single task. I could never find any of my files in a timely fashion because there was no program that would enforce on my behalf a predictable file-naming scheme.
All my notes were eventually not retrievable. My music collection was inconsistent because I needed special software to write the metadata… In short, I was not as productive as I would like to be. And, above all, it was not fun.
Most of my work at the time was centred around the email client and a word processor. The email client had its own subsystem for handling reminders for tasks. The format of those tasks was not interoperable with other programs. I could not access it with my favourite text editor. I thus had to use the clunky interface of the email client, which was never designed for task management—and was not configurable.
And then I had all the cognitively burdensome annoyances of my two applications looking quite different from each other. My emails did not behave like my documents, which made it harder for me to flip between the two and continue writing.
Emacs has elevated my computing experience. I have been much more productive ever since I switched to it. Allow me to demonstrate a tiny bit of what I do each day. I will temporarily exit the presentation mode in this window. Then, in the bottom half of my screen, I will open my email client to read a message I got. After that I will switch to my agenda to record a task and review what I have to do. All this is done inside of Emacs. Time for action!
What I just demonstrated is a very small part of what I do every single day. There is much more, though I cannot cover it all in this presentation. The point, however, is the consistency of the experience; consistency throughout.
I have customised my email client by writing some Emacs Lisp code for it. I have done the same for the custom agenda I have. And much more.
Every time I work with Emacs Lisp, I acquire skills that are applicable outside the confines of the problem I am solving. For example, by configuring email the way I want, I pick up programming skills that I can then apply to the design of my custom agenda.
This is an investment that pays off more and more. Emacs will grow or shrink to match my evolving needs. Each new workflow I incorporate in my Emacs setup will thus benefit from all the knowledge and features I have accumulated.
I do not have to relearn everything. I do not have to throw away all the work I did. It is here to stay. I do not feel the pressure to try the new shiny app of the day. And, because I am rooted in this stability, I remain productive and efficient.
I mentioned earlier that Emacs is free or libre software. This means that you can read its source code, modify it, and share your changes with others. Emacs has a license that gives users power. There is no corporation that can take Emacs away from us. It belongs to the community and we all tend to its wellness.
Software freedom is not just about the license. Emacs makes such freedom an irriducible part of its functionality. You can, at any moment, ask Emacs what does a keyboard shortcut actually do. What is the definition of a function. What is the value of a variable. And you may even access the source code to check for yourself.
I actually learnt to program in Emacs Lisp by exercising this freedom. I would tinker with Emacs and continuously check on its state. I wanted to learn how, for example, we move down a line. From there, I learnt that we can move down many lines at once. I then figured that we can move down the lines and then also do something else, such as place the cursor at the end of the line and create a pulse effect to bring attention to it.
Not only did I learn how to configure Emacs, I even wrote tens of extensions for it. I have also authored a libre book titled “Emacs Lisp Elements”. This freedom is not theoretical. I did not have a background in programming, yet was empowered to act.
Emacs is extended with Emacs Lisp. If you know how to program in that language, you can be extra opinionated and particular about the way Emacs facilitates your work.
But even without any expertise of this sort, you can still do much of what you like. This is because the Emacs community has developed a rich corpus of extensions. We call these extensions “packages”, as they are distributed in a way that makes them easy to install and then use directly.
The Emacs program you will download on your computer ships with plenty of packages built-in. Depending on your needs, you may not even have to install anything from what the community has to offer.
For example, Emacs ships with a package called org or “Org mode”. At
its core, this is a markup language. I am using it right now in this
document. Notice how lines that start with an asterisk function as
headings. This is what the markup does. Org lets you write documents,
including books, handle your tasks, organise your agenda, and much
more. It is a powerhouse. There are so many things to discover. Emacs
provides high quality documentation that explains everything.
When you install Emacs, you get with it plenty of technical manuals. There is also an interactive tutorial to help you make sense of the basics. Furthermore, when you ask Emacs for help about the definition of a function or the value of a variable, you receive the documentation for the thing you are looking for.
The expectation for all contributions to the official Emacs program is that the code is well-documented and the manual is updated accordingly.
This is true also for packages that the community develops. For
example, my denote package has a manual that is over 7500 lines
long. It exceeds 52000 words. In it users find detailed instructions
as well as code snippets that they can copy and use outright. And this
is not the exception. All my packages are like that, to the extent
necessary. Most other developers do the same.
As a community, we have access to so much knowledge for free and in freedom. If we are committed enough, we can learn from others and thus become better ourselves. We do so in a spirit of sharing and caring. For me, specifically, all this was of great help. I am self-taught because I received all those great resources from the community. I consider it my duty to give back in kind.
Because Emacs is extensible, there is practically no limit to what you can do with it. At least this is the case for all tasks that are text-heavy. Emacs will just gracefully evolve to match your requirements, provided you know how to extend it.
The downside, however, is that it is not easy to become proficient in it. If you are committed, you can learn the basics within the first few days. Though you will need to invest a few weeks or months to become skillful. It depends on how much effort you put into it.
What I can say with confidence is that Emacs is not for tourists. You cannot switch to it with the expectation that you will have a good time right away. No. That will not work. There simply is no shortcut to excellence.
I thus encourage you to adjust your expectations. Emacs will make you more productive, provided you are patient enough to unlock its virtually boundless potential. Take it slow and be methodical. Rely on the official manual. Read from it and gradually incorporate its insights into your workflow. The community has plenty of resources to complement that study. But do not skip the official manual. Learning it slowly means that you will become proficient faster than you otherwise would.
I already talked about the technical side of things with regard to the integrated computing environment. Now combine that with two facts:
Emacs is not old, it is timeless. This is because it can be extended in a spirit of freedom. Whatever new technology or idea we have as a collective, we can eventually bring it into Emacs. This way, our integrated computing environment adapts with the times.
Couched in those terms, the initial effort you will put into learning Emacs is actually not that much. You have to maintain a longer-term view of this project. If are patient, Emacs will be one of the most reliable tools you will ever use throughout your life. And I say this as a handy man myself, having built the house I am in, among others.
I switched to Emacs in the summer of 2019. It is almost 7 years already. I see no reason not to use it for the next 7 years, if I can. I will still want to write articles, do programming, maintain my agenda, and probably make presentations like this one.
Remember that you will not learn Emacs over the weekend. You are in it for the long-term. Take it slow and you will enjoy the experience.
This is all I have for you today folks. Thank you very much for your attention!
]]>The presentation will be about 40 minutes long. I will then answer any questions from the audience. Anyone can participate: no registration is required. The event will be recorded for future reference. The link for the video call and further details are available here: https://ox.ogeer.org/event/computing-in-freedom-with-gnu-emacs-protesilaos-stavrou.
I will prepare a transcript for my talk. This way people can learn about my presentation without having to access the video file.
Looking forward to it!
]]>doric-themes package:
doric-almond (light)doric-coral (light)doric-magma (dark)doric-walnut (dark)Each of them has its own character, while they all retain the minimalist Doric style. Below are some screenshots. Remember that these themes use few colours, relying on typography to establish a visual rhythm.
All four themes are in development. I may still make some minor
refinements to them, though I have already defined their overall
appearance. If you like the minimalism of the doric-themes, I think
you will appreciate these new additions.
The Doric themes use few colours and will appear monochromatic in many contexts. They are my most minimalist themes. Styles involve the careful use of typographic features and subtleties in colour gradients to establish a consistent rhythm.
If you want maximalist themes in terms of colour, check my ef-themes
package. For something in-between, which I would consider the best
“default theme” for a text editor, opt for my modus-themes.
doric-themesI had a ~2-hour chat with Joshua Blais, a fellow Emacs user, over at the @JoshuaBlais YouTube channel: https://www.youtube.com/@JoshuaBlais. We covered Emacs at length and also talked about general life issues.
The first topic we cover is how to place constraints on yourself in order to avoid backsliding into bad habits. This ties in to the themes of discipline and productivity that we discuss in some further length.
Joshua asks me how I got into Emacs and how I started writing/maintaining packages for it. We talk about how Emacs provides for an integrated computing experience. Learning Emacs Lisp allows you to have better control of Emacs.
In this light we comment on Guix and how it is also configurable in a dialect of Lisp. Joshua is using Nix and I learn more about that experience.
Coming back to Emacs, we comment on its relationship to the Unix philosophy. I think Emacs is compatible with Unix. Though my main point is how Emacs empowers us to use the computer in a productive way. It augments the experience.
Simple living and financial independence is another topic we cover. Joshua wants to know how I approach this issue. I explain how it is a matter of controlling your wants. Figure out what the parts of your lifestyle that you would not sacrifice. Then you know how much money you need for that lifestyle.
Joshua makes a connection of the simple life to Emacs and Unix tools. I comment on that as well. Once you start using Emacs and friends, you learn to appreciate the essentials. This you can then apply to other parts of your life.
We move to note-taking, where I comment about Denote. I explain how it is a file-naming scheme, which can also be used to write notes. What matters is how well we can retrieve information. Joshua explains how pen and paper helps him express his thoughts.
Learning on your own is our next point. Being an autodidact myself, I comment how it empowers you. You are able to have initiative and be more independent.
We then explore how things have infinite depth. This is how everything in the world is connected. This also relates to the point about the simple living, since you can have relatively few things that you keep understanding in depth.
Joshua asks me about discipline. This is a capacity we can build up. I give some examples.
Next on our list are mechanical keyboards. Joshua and I are using a split keyboard.
Then we explore the theme of using tools the right way. One example is the Internet as a whole. Another is with LLMs. It helps to know “why am I doing this”, as then you can understand when you are meeting your goals and when you are moving away from them. We explore this in further depth.
I comment on a common mistake we make where we think that the complex must be sophisticated and profound. Whereas there is profundity in simplicity.
We connect the dots through all these as we wrap things up.
Thanks to Joshua Blais for this chat. I had a good time and wish him all the best!
]]>This has become easier since Emacs 29, which introduced a command-line
flag called --init-directory. It is responsible for loading the
init.el that is present in the given directory. For example:
# From a terminal or shell, run something like this:
emacs --init-directory=/tmp/test-emacs/
In other words, you can keep your regular configuration intact while launching Emacs with another set of options.
Have a directory that is unrelated to your regular Emacs
configuration. Then write the init.el inside of it.
Because I do this frequently, I prefer to use the standard Linux path
/tmp/. Its files get deleted as soon as I switch off the computer,
which is exactly what I want in this case.
As such, if there is a bug with, say, the modus-themes, I will work
with this file path /tmp/modus-themes/init.el.
But the exact location of the directory does not matter, so choose what makes sense to you.
In that init file, include only the code that is needed to reproduce the bug.
Since you want to have the package installed, it makes sense to write
a use-package declaration for it. Include the :ensure t directive
as it instructs the built-in package manager to install the package if
it is not already available.
;; Contents of the init.el...
(use-package modus-themes
:ensure t
:config
(setq modus-themes-common-palette-overrides
'((fringe unspecified)
(border-mode-line-active unspecified)
(border-mode-line-inactive unspecified)))
(setq modus-vivendi-palette-overrides
'((bg-main "#1e1f22")
(fg-main "#bcbec4")))
(load-theme 'modus-vivendi t))
If you are using an alternative to package.el like straight or
elpaca, then the aforementioned :ensure t will likely not suffice:
you need to build the package from source. To this end, Emacs has the
function package-vc-install. Some of my recent packages have sample
code that relies on this approach. For instance:
(use-package gnome-accent-theme-switcher
:demand t
:init
;; Then upgrade it with the command `package-vc-upgrade' or `package-vc-upgrade-all'.
(unless (package-installed-p 'gnome-accent-theme-switcher)
(package-vc-install "https://github.com/protesilaos/gnome-accent-theme-switcher.git"))
:bind
(("<f5>" . gnome-accent-theme-switcher-toggle-mode)
("C-<f5>" . gnome-accent-theme-switcher-change-accent))
:config
(gnome-accent-theme-switcher-mode 1))
In the above snippet package-vc-install will pull the latest commit
from the main branch, though it can even get a specific commit. Read
its documentation with M-x describe-function.
What matters is that you fetch the version which you are running in your personaly configuration.
From the command-line, run something like the following:
emacs --init-directory=/tmp/test-emacs/
This will launch a new instance of Emacs. The use-package you placed
there will do the work to install the package. After that you are
ready to reproduce the bug in this clean setup.
To help the maintainer identify the source of the trouble, keep a record of all the steps you followed. Some bugs show up when the package is loaded, but others are triggered only after a specific action is performed.
Normally, Emacs will pop up a *Backtrace* buffer when it encounters
an error. Copy its contents and send them to the maintainer, together
with the init.el you used, and the list of the steps you followed.
It sometimes happens that you install a package and it is completely broken. Although this looks bad, it may not even be a bug, but an issue with the old bytecode you had on your system from the previous version of the package.
Do M-x package-delete, select the package, restart Emacs, and then
M-x package-install to install the package anew. If everything
works, then the problem is gone and you do not need to tell the
maintainer about it.
With this knowledge, you can provide high quality bug reports for the packages you rely on. Good luck!
]]>On Thursday, the 12th of March 2026, at 6 PM United Kingdom time (GMT) I will give a talk titled Computing in freedom with GNU Emacs. My intention is to introduce Emacs to a wide audience. Participation is open to everyone.
I will post a reminder as we get closer to the date of the event. Looking forward to it!
]]>institution-calendar package for Emacs displays term+week
indicators in the *Calendar* buffer (which, by default, is produced
by the calendar command). This is useful, for example, for schools
who organise their work by terms, such as winter, spring, and summer,
with each having a set number of weeks. This is how the University of
Oxford will look like:
The package supports the universities of Oxford and Cambridge out-of-the-box, though users can define their own institutions. I have two institutions there to provide concrete examples. I am happy to add more, but the idea is for users to maintain their own data.
Below I show a complete example using data for a school in Austria.
To make this work, you first need to specify the data. This has the
same structure as institution-calendar-oxford-university-dates and
thus passes the test of institution-calendar-valid-data-p. Look at
the code for Oxford to get an idea. Here is a sample:
(defvar my-austrian-school-dates
'((2025 (wintersemester ( 9 8 2025) ( 2 6 2026))
(sommersemester ( 2 16 2026) ( 7 10 2026)))))
This is an Austrian school that has two terms for the academic year
starting in 2025: wintersemester and sommersemester. The symbols
for those terms can be anything. Internally, the package uses the
first letter to form the week indicator, followed by the number of the
week within the given term.
Each term defines a start date and an end date as a list of integers
of the form (MONTH DAY YEAR). I picked a form that is consistent
with the way calendar.el represents the date, otherwise I would have
used a different standard.
The variable institution-calendar-user-entities contains all
user-defined institutions and their corresponding calendar data. Each
entry is a cons cell of the form (ENTITY . CALENDAR-DATA), where
ENTITY is an arbitrary symbol and CALENDAR-DATA is the symbol of a
variable that holds the data, as shown in the previous section.
Here is how we can add to this list:
(add-to-list 'institution-calendar-user-entities (cons 'austrian-school 'my-austrian-school-dates))
In this example, I am calling the newly registered institution
austrian-school, which is how I can refer to it elsewhere. I am
associating this austrian-school with the calendar data of the
variable my-austrian-school-dates.
institution-calendar-mode work for your institutionWith the aforementioned in place, the user option institution-calendar-entity
can be set to the value of austrian-school:
(setopt institution-calendar-entity 'austrian-school)
If the institution-calendar-mode is enabled, then the regular
calendar command will display week indicators for this school. This
is good if you only need one calendar. But if you work with many
institutions and thus need to switch between their calendars, then
ignore this step and move to the next one. Or ignore it anyway if you
prefer to keep the M-x calendar intact.
The macro institution-calendar-define-convenience-command makes it
trivial to define a command that produces a calendar buffer for the
given institution. This is like M-x calendar with the minor mode
institution-calendar-mode enabled, except it does not alter the
output of the calendar—so you can use them both (or, anyhow, use
as many as the institutions you care about).
;; This defines the command `institution-calendar-austrian-school'.
;; Call `institution-calendar-austrian-school' with M-x or bind it to a key.
(institution-calendar-define-convenience-command austrian-school)
Once you evaluate this macro call, you will get the command
institution-calendar-austrian-school. Use that to produce a calendar
that works with the austrian-school in particular. If you are
curious, M-x institution-calendar-oxford-university will still do
the right thing for the University of Oxford. Same for the command
institution-calendar-cambridge-university.
The user option institution-calendar-include-intermonth-header adds
a header above the week numbers. By default, this only works with the
universities of Oxford and Cambridge. Though you can extend the
package to support your institution by adding to the value of the
variable institution-calendar-intermonth-headers. Thus:
(add-to-list 'institution-calendar-intermonth-headers (cons 'austrian-school "AU"))
You can skip this step if you do not plan to display the intermonth header. Those are not shown by default.
This is how your configuration of the institution-calendar may look like:
(use-package institution-calendar
:ensure nil ; not in a package archive
:init
;; Install it from source.
;; Then upgrade it with the command `package-vc-upgrade' or `package-vc-upgrade-all'.
(unless (package-installed-p 'institution-calendar)
(package-vc-install "https://github.com/protesilaos/institution-calendar.git"))
:config
(defvar my-austrian-school-dates
'((2025 (wintersemester ( 9 8 2025) ( 2 6 2026))
(sommersemester ( 2 16 2026) ( 7 10 2026)))))
(add-to-list 'institution-calendar-user-entities (cons 'austrian-school 'my-austrian-school-dates))
;; This defines the command `institution-calendar-austrian-school'.
;; Call `institution-calendar-austrian-school' with M-x or bind it to a key.
(institution-calendar-define-convenience-command austrian-school)
(setopt institution-calendar-include-extra-week-numbers t)
(setopt institution-calendar-include-intermonth-header nil)
;; These are optional, if you want `M-x calendar' to work for your institution.
(setopt institution-calendar-entity 'austrian-school)
(institution-calendar-mode 1))
In this short video I demonstate a new package for GNU Emacs that synchronises the Emacs theme with the GNOME settings for accent color and light/dark mode. Git repository here: https://github.com/protesilaos/gnome-accent-theme-switcher.
]]>hl-line-mode. It
remaps the hl-line face (or equivalent) buffer-locally to a style that
is optimal for major modes where line selection is the primary mode of
interaction.
The idea is that hl-line-mode cannot work equally well for contexts
with competing priorities: (i) line selection, or (ii) simple line
highlight. In the former case, the current line needs to be made
prominent because it carries a specific meaning of some significance in
the given context: the user has to select a line. Whereas in the latter
case, the primary mode of interaction does not revolve around the line
highlight itself: it may be because the focus is on editing text or
reading through the buffer’s contents, so the current line highlight is
more of a reminder of the point’s location on the vertical axis.
linBelow are the release notes
This is the first tagged release since 2024-08-05. The package is in a stable state: it does everything it is meant to. This version makes some small refinements, mostly in how parts of the code are written. Though there also are some nice user-facing changes:
The lin-gnome-accent-color-mode synchronises the accent colour of
the GNOME desktop environment with Lin’ own lin-face. This happens
live, so any buffers that are already using the lin-mode (directly
or via lin-global-mode) will get the updated colour.
The user option lin-gnome-accent-color-override-foreground
controls whether the faces that correspond to GNOME accent colours
should override the underlying text colour or not. This is useful
for improved colour contrast. The default is to not override the
foreground. Setting lin-gnome-accent-color-override-foreground to
non-nil changes that so, for example, the lin-face will be set
to lin-red-override-fg instead of lin-red (of course, faces can
be modified by users/themes to override the foreground anyway, so
this is about the default behaviour).
New faces to style the current line when lin-mode is enabled
include lin-purple, lin-orange, and lin-slate. Those do not
override the underlying foreground colours by default. Whereas
lin-purple-override-fg, lin-orange-override-fg, and
lin-slate-override-fg apply their background while also setting
the foreground (remember that you always control which face to use
by changing the user option lin-face).
The lin-global-mode skips all private buffers. These are buffers
that users normally do not interact with directly. Their names are
prefixed with a space and, by default, are hidden from the view of
switch-to-buffer and related commands.
The default value of the user option lin-mode-hooks now includes
the world-clock-mode-hook and xref--xref-buffer-mode-hook. The
former relates to the command world-clock, while the latter is
used by any command that produces Grep-like results via the built-in
Xref infrastructure (for example, my Denote package does that for a
few of its commands). The lin-mode-hooks is a list of hooks for
major modes that should use the Lin style for the selection line
highlight.
This is about a new package of mine: institution-calendar. It is not
going to be available on GNU ELPA. Users will have to install it from
source (code for this is further below). The reason is that the
predecossor to this package, oxford-calendar, was not accepted:
I will consider my options going forward, with whatever that means for all my packages.
The institution-calendar package augments the M-x calendar buffer
to include indicators about the applicable term. Each term has week
numbers, which are displayed on the side of the regular calendar data.
The user option institution-calendar-entity specifies which
institution’s data to use. Currently, the value can be either
oxford-university or cambridge-university. Contact me and I will
add support for your institution.
Each term shows the week numbers it formally defines. For example, the
University of Oxford has three terms of 8 weeks each. When the user
option institution-calendar-include-extra-week-numbers is set to a
non-nil value, then an additional two weeks are added: week 0 for
one week before the term starts and an extra number after the term
ends. This is useful for scheduling purposes, such as to arrange
meetings in preparation of the work ahead or to report on what
happened.
The user option institution-calendar-include-intermonth-header
writes text above the institution’s week indicators. This makes it a
bit easier to tell them apart from the regular calendar data.
Enable the minor mode institution-calendar-mode to make all future
calls to M-x calendar use the relevant institution data.
If you do not want to affect the M-x calendar output, then use the
command institution-calendar: it is functionally equivalent to
having the aforementioned minor mode enabled, except it has no
permanent effect on M-x calendar—that will keep its original
appearance.
If, for whatever reason, you need to check the calendar of a specific
institution, then do M-x institution-calendar-cambridge-university
or M-x institution-calendar-oxford-university (more such commands
will be available to match any other institutions that this package
will support).
(use-package institution-calendar
:ensure nil ; not in a package archive
:init
(unless (package-installed-p 'institution-calendar)
(package-vc-install "https://github.com/protesilaos/institution-calendar.git"))
:commands
(institution-calendar
institution-calendar-cambridge-university
institution-calendar-oxford-university)
:config
(setopt institution-calendar-entity 'oxford-university)
(setopt institution-calendar-include-extra-week-numbers t)
(setopt institution-calendar-include-intermonth-header nil)
;; If you want to permanently change what M-x calendar shows, enable
;; this mode. Otherwise, use the relevant command from the
;; :commands listed above.
(institution-calendar-mode 1))
If you want maximalist themes in terms of colour, check my ef-themes
package. For something in-between, which I would consider the best
“default theme” for a text editor, opt for my modus-themes.
doric-themesBelow are the release notes.
doric-jade is a light theme with a predominantly green feel.
doric-copper is a dark theme with orange, magenta, and cyan colours.
I have update all screenshots: https://protesilaos.com/emacs/doric-themes-pictures.
The relevant faces now use a colour-coding scheme where TODO states are rendered in red while DONE are green.
This is done to ensure cross-theme consistency. Those faces communicate a certain state and, therefore, it is better to not have to relearn which colour means what while switching between the Doric themes.
~code~ faces stand out moreThis is done to differentiate them from =verbatim=. It is especially
important for users who choose to hide the markup with the user option
org-hide-emphasis-markers (I used to do that but realised that the
ambiguity was a problem in many cases, because ~code~ and =verbatim=
have different semantics in some exported formats).
This covers the regular Org headings as well as anything that performs the same function, such as in the Org agenda buffer.
All regular headings use the main foreground value. The document title and the Org agenda equivalent of that are rendered in an accent colour for greater effect.
Combined with the aforementioned revision of the TODO and DONE states, Org buffers are easier to read and work with.
The faces that communicate the current time and applicable filters are made more intense. The idea is to spot them more quickly.
Faces that pertain to diary or diary-style events no longer use italics to avoid exaggerations.
Blocked tasks are easier to spot.
The applicable query in the structure header is made more prominent.
Overall, Org agenda buffers should be easier to scan.
The active/available keys in the Org export dispatcher use more intense colours and have greater padding around them. This is what I also do with the Modus themes (and all derivatives) to improve the legibility of those keys.
The authors in log views and elsewhere have a distinct colour to stand out a bit more. Branches use consistent typography, while the current branch stands out more than the others.
Enabled and disabled keys use a green-red coding scheme.
Active values and arguments have a style that is the same across themes for the same reason as Org TODO and DONE.
Transient headings use the main foreground colour to not draw more attention than they need to.
Those are seen when writing a commit message in Magit.
The relevant face uses a distinct foreground. It no longer applies a bold weight, as that had the effect of making the buffers much busier than necessary.
Items that are marked for selection are rendered in a green style, while those marked for deletion are red.
This is done for cross-theme consistency, so that users do not have to think twice before performing the relevant operations.
doric-themes-with-colors macro for advanced usersThis macro is effectively the same as a let for binding the colours
of the active Doric theme. Advanced users can rely on this macro to
write functions that, for example, set the theme-specific red colour
value of a given face.
Symlinks in Dired buffers are easier to spot. Same for visited links in Info buffers.
Org definitions no longer override the foreground of any other face present in them. Same for the notmuch header in view buffers.
]]>doric-themes package.
The collection is growing to cover styles that range from austere to
playful.
doric-jade is a light theme with an emphais on green. doric-copper
is a dark theme with mostly orange accents and hints of patina. Both
themes retain the Doric quality of using few colours while relying on
typography to establish rhythm and structure.
Below are their samples.
[ Or just check all the pictures: https://protesilaos.com/emacs/doric-themes-pictures. ]
doric-themes version 0.7.0Both doric-jade and doric-copper are in-development. I have
already worked on their finer point, but may still make some tweaks
before publishing them as part of the next stable version of the
doric-themes.
The Doric themes use few colours and will appear monochromatic in many contexts. They are my most minimalist themes. Styles involve the careful use of typographic features and subtleties in colour gradients to establish a consistent rhythm. Legibility is still high.
If you want maximalist themes in terms of colour, check my ef-themes
package. For something in-between, which I would consider the best
“default theme” for a text editor, opt for my modus-themes.
doric-themesbeframe enables a frame-oriented Emacs workflow where each frame has
access only to the list of buffers visited therein. In the interest of
brevity, we call buffers that belong to frames “beframed”.
beframeBelow are the release notes
This version fixes two bugs and makes other minor tweaks.
The first bug pertains to the performance of the command
beframe-switch-buffer or the command switch-to-buffer when
beframe-mode is enabled: they were really slow when the list of
buffers was long. Now they are always fast. Thanks to Alexandre
Rousseau for reporting the problem in issue 17:
https://github.com/protesilaos/beframe/issues/17.
The second bug is more subtle. It is about persisting the completion
metadata category value in all prompts that read a buffer, when
beframe-mode is enabled. This change means that users who configure
the user option completion-category-overrides will not get the
expected results in buffer prompts affected by Beframe.
Thanks to Stefan Monnier for commenting on the initial implementation,
specifically telling me that let binding the metadata can affect
nested minibuffers, which we do not want. This was done on the
emacs-devel mailing list: https://lists.gnu.org/archive/html/emacs-devel/2025-12/msg00264.html.
ef-themes are a collection of light and dark themes for GNU
Emacs that provide colourful (“pretty”) yet legible options for users
who want something with a bit more flair than the modus-themes (also
designed by me).
ef-themesBelow are the release notes.
This version introduces two carefully designed, legible and colourful
themes: ef-orange (light) and ef-fig (dark). Both draw inspiration
from the fruits they are name after.
The convenience commands ef-themes-select-dark and
ef-themes-select-light use the minibuffer to select+load an Ef theme
that is either dark or light, respectively. The more general command
is ef-themes-select.
All screen shots of the themes are updated to reflect their current status: https://protesilaos.com/emacs/ef-themes-pictures.
Remember that since version 2.0.0 the ef-themes are built on top
of my modus-themes. This means that most of the changes happen to
Modus and are inherited by Ef.
tmrBelow are the release notes.
This version adds some user options and new features to an already stable package.
The command tmr-toggle-pause will prompt for a running timer and
pause it.
Users who have something like the following in their configuration,
have access to tmr-toggle-pause under the P key:
;; All TMR commands are behind this prefix key. So `tmr-toggle-pause' is C-c t P.
(define-key global-map (kbd "C-c t") #'tmr-prefix-map)
In the buffer produced by the command tmr-tabulated-view the pause
functionality applies to the timer at point. The tmr-toggle-pause is
invoked with the P key.
An extra column in the tabulated view shows whether a timer is paused or not. Here is an example:
Start End Duration Remaining Paused? Acknowledge? Description
08:49:41 09:19:46 30m 29m 17s Yes Work on TMR for 30 minutes
08:49:31 08:54:31 5m 3m 53s Prepare tea
08:49:21 08:59:21 10m 8m 42s Yes Edit the description with this one instead
When a timer is set to be acknowledged (i.e. the user must confirm
that they saw it elapse) it prompts for confirmation. The default
input text that confirms the acknowledgement is ack. This is now
subject to configuration via the user option tmr-acknowledge-timer-text.
The buffer produced by tmr-tabulated-view is set to automatically
refresh every 5 seconds by default. In previous versions this was every
1 second. The new user option tmr-tabulated-refresh-interval can be
set to a number of seconds or nil. In the latter case, the automatic
refresh is disabled.
tmr-tabulated.elIts code was merged into tmr.el on 2024-10-30 and all it was doing
thenceforth is issue a warning to those who would require the
tmr-tabulated feature. Now (require 'tmr-tabulated) produces an
error.
In this ~15-minute video I demonstrate a package of mine called tmr
(pronounced as an acronym or as “timer”). It uses a simple notation to
set the duration of a timer at the minibuffer prompt. Once the timer
elapses, Emacs shows a notification. The desktop environment will also
include one, as well as an audio alert (those are configurable).
Timers can optionally have a description. They may also be listed in a
tabulated/grid view, which makes it easier to work with them (to edit
their description, reschedule them, etc.). Running timers may also be
displayed on the mode line.
(use-package tmr
:ensure t
:config
(define-key global-map (kbd "C-c t") #'tmr-prefix-map)
(setq tmr-sound-file "/usr/share/sounds/freedesktop/stereo/alarm-clock-elapsed.oga"
tmr-notification-urgency 'normal
tmr-description-list 'tmr-description-history))
tmrnotmuch
index on the Emacs mode line. The underlying mechanism is that of
notmuch-count(1), which is used to find the number of items that
match the given search terms. In practice, the user can define one or
more searches and display their counters. These form a string which
realistically is like: @50 😱1000 ♥️0 for unread messages, bills, and
fan letters, respectively.
notmuch-indicatorBelow are the release notes.
This version adds quality-of-life refinements to a stable package.
notmuch-indicator-mode sets up the notmuch-after-tag-hookThe indicator will be updated whenever a message’s tags change. This way users do not need to rely on the timer-based method that we have always had.
notmuch-indicator-refresh-count can be set to nilDoing so has the effect of disabling the timer-based refresh of the
indicator. It will now be updated only when some event happens, such
as with the aforementioned change to tags or after the invocation of
any of the commands listed in the user option notmuch-indicator-force-refresh-commands.
When checking for the notmuch configuration file, we now also
consider these two filesystem paths:
$HOME/.config/notmuch/$NOTMUCH_PROFILE/config$HOME/.config/notmuch/default/configThanks to Yejun Su for the contribution in pull request 6: https://github.com/protesilaos/notmuch-indicator/pull/6.
The change is small, meaning that Yejun Su does not need to assign copyright to the Free Software Foundation.
]]>If you want maximalist themes in terms of colour, check my ef-themes
package. For something in-between, which I would consider the best
“default theme” for a text editor, opt for my modus-themes.
doric-themesBelow are the release notes.
This version adds support for more packages, while it revises some of the faces that were already covered.
The minibuffer prompt used by the command doric-themes-select now
groups themes by their light or dark type. The current theme is at the
top.
The highlights generated by the various commands of the avy package
now have a slightly more intense background+foreground colour combination.
It should be easier to spot and to differentiate from other highlights
such as that of hl-line-mode and the mouse hover effect over links.
My tmr package styles timers in its grid/tabulated interface as well
as on the mode line. All these now get colours that come directly from
the active Doric theme. Before, the colours were defined only in the
tmr source code: they were “okay” (because they are based on my
modus-themes) but not stylistically optimal.
ruler-modeThe built-in ruler-mode draws a ruler at the top of the current
buffer. All of its faces now use appropriate colours.
All packages that have a face that is about highlighting trailing spaces now get a red colour value that is more appropriate for each Doric theme.
I removed an override for the built-in shr-text face, which was
making the nov package display its buffers in a monospaced font.
Thanks to Marcus Kammer for telling that nov-mode buffers were not
proportionately spaced by default. This was done in issue 23:
https://github.com/protesilaos/doric-themes/issues/23.
institution-calendar:
https://protesilaos.com/codelog/2026-02-11-emacs-institution-calendar/.
The oxford-calendar is a small package that is of interest to
students, academics, and staff of the University of Oxford. It
augments the M-x calendar buffer with indicators that show the
applicable term (Michaelmas, Hilary, Trinity) and week number.
To show the indicators, enable the oxford-calendar-mode. By default,
it includes the extra weeks 0 and 9 at the boundaries of each term.
The idea is to make things easier for planning purposes. Remove those
extra weeks by setting oxford-calendar-include-extra-week-numbers to
nil.
To include a heading above the term indicators, set the user option
oxford-calendar-include-intermonth-header to a non-nil value.
I will now do the work to include the package on the GNU ELPA archive.
oxford-calendar (!!! COMING SOON)narrow-to-defun), (iii) from point to the end of the buffer, and
(iv) from point to the beginning of the buffer. Variations of these
scopes are also available.
These substitutions are meant to be as quick as possible and, as such,
differ from the standard query-replace (which I still use when
necessary). The provided commands prompt for substitute text and
perform the substitution outright, without moving the point. The
target is the symbol/word at point or the text corresponding to the
currently marked region. All matches in the given scope are
highlighted by default.
substituteBelow are the release notes.
This is a small release that fixes a bug and adds a relevant user option.
The bug pertained to the scope of the substitution when buffer narrowing was in effect. All commands would ignore narrowing and also fail to properly clear the highlights they apply (highlights are transiently in effect to show what the target of the substitution is while the minibuffer is waiting for user input).
Now all commands do the right thing with respect to buffer narrowing.
Though their exact behaviour depends on the value of the new user
option substitute-ignore-narrowing:
When the value of substitute-ignore-narrowing is non-nil, then
substitutions apply to the actual scope of the given command. For
example, substitute-target-in-buffer will cover the whole buffer
from the absolute minimum position to the absolute maximum position
even if narrowing is in effect.
When the value of substitute-ignore-narrowing is nil, then
substitutions apply to their scope subject to the boundaries of the
narrowed buffer. For example, substitute-target-in-buffer will
understand as “whole buffer” the region between the minimum and
maximum positions of the narrowed buffer.
Users can write small convenience commands that do either of those, depending on preference. For example:
(defun my-substitute-target-in-buffer-with-narrowing ()
"Call `substitute-target-in-buffer' with `substitute-ignore-narrowing' as `nil'."
(let ((substitute-ignore-narrowing nil))
(call-interactively 'substitute-target-in-buffer)))
(defun my-substitute-target-in-buffer-without-narrowing ()
"Call `substitute-target-in-buffer' with `substitute-ignore-narrowing' as non-`nil'."
(let ((substitute-ignore-narrowing t))
(call-interactively 'substitute-target-in-buffer)))
Thanks to zauberen for reporting the bug in issue 11: https://github.com/protesilaos/substitute/issues/11.
]]>ef-themes package. ef-orange is
a light theme with a noticeably tinted background that combines
orange, yellow, and green hues. ef-fig is a dark theme with a dark
purple background that relies on green, yellow, and pink hues.
Sample pictures are available below. Check all the items here: https://protesilaos.com/emacs/ef-themes-pictures. Always click to enlarge the image for best results.
Both themes are done. Though there may still be some minor
refinements. They will be widely available as part of the next stable
version of the ef-themes package (maybe end of January 2026).
Remember that since version 2.0.0, the ef-themes are built on top
of my modus-themes. This means that they are highly customisable and
support a wide range of packages and face groups.
Enjoy!
The ef-themes are a collection of light and dark themes for GNU
Emacs that provide colourful (“pretty”) yet legible options for users
who want something with a bit more flair than the modus-themes (also
designed by me).
ef-themesmodus-themes (also built into Emacs 28+)This version fixes some bugs, adds a new feature for those who want to derive a theme from Modus, and makes other small quality-of-life refinements.
modus-themes-with-colors should work at all timesIn the transition to version 5.0.0, I inadvertently introduced
regressions to the behaviour of the modus-themes-with-colors macro.
This macro let binds the current theme’s palette around arbitrary
Elisp expressions, which allows users to access the named colours
therein. In versions 5.0.0 and 5.1.0 the macro could not read
variables defined outside its scope. Users needed to write an eval
around it, which I did not like. Now the macro should not require such
workarounds: it basically is a let that should work as expected
everywhere.
This was fixed over a series of Git commits related to issue 170: https://github.com/protesilaos/modus-themes/issues/170. Thanks to Alexandr Semenov and realazy for reporting the problems and testing my revisions.
modus-themes-generate-palette function to quickly get a paletteUsers or package developers who want to create a theme on top of Modus
can now get a kickstart by defining their palette with the help of the
new modus-themes-generate-palette function. This function is meant
to return a complete palette, given a list of basic colours. Users can
thus experiment with their new theme while knowing that what they got
contains all the definitions; definitions that they may then modify
further (e.g. to define different semantic mappings than the defaults
such as, for example, to have (fg-heading-1 red-warmer) instead of
what originally is (fg-heading-1 fg-main)).
I have written extensive documentation in the manual, which includes a complete example of a Solarized theme that is built on top of Modus. If you have any questions, you are welcome to contact me.
The commands modus-themes-select-dark and modus-themes-select-light
use minibuffer completion to load a theme. The completion candidates
are filtered to only dark or light themes, respectively.
This is effectively the same as calling the command modus-themes-select
with a prefix argument (C-u by default).
Remember that we also have the commands modus-themes-load-random,
modus-themes-load-random-dark, and modus-themes-load-random-light.
Otherwise use the command modus-themes-rotate.
The minibuffer prompt used by the various Modus commands to select a
theme now has a grouping function in place: it shows the current theme
at the top and then all other themes grouped by their dark or light
background. This makes it easier to find a relevant theme, especially
if lots of them are present, such as when modus-themes-include-derivatives-mode
is enabled and relevant packages/themes are available (e.g. my
ef-themes and standard-themes).
transient.el (e.g. in Magit)The transient.el concept of “semantic colours” is now supported.
This is used by default in Magit to denote the different types of
keys, such as those that exit the transient, keep it active, move to
another transient, and the like. Users who prefer the old style where
all key bindings looked the same must customise the user option
transient-semantic-coloring.
Note that the deuteranopia- and tritanopia- optimised themes adapt gracefully to such “semantics”, owning to relevant internal refinements I made. Those themes cannot rely on the full colour spectrum to communicate such nuances.
hl-todo-mode faces use a bold weight if appropriateWhen the user option modus-themes-bold-constructs is set to a
non-nil value, then all keywords that hl-todo-mode highlights will
be rendered in a bold weight (technically, they inherit the bold
face). This is how we were doing it before until I undid it by
mistake. Thanks to Dominik Schrempf for reporting the bug in issue
177: https://github.com/protesilaos/modus-themes/issues/177.
The Gnus mail groups no longer have hardcoded colour values. They will look different depending on the current Modus theme.
:box attribute handle unspecified coloursI updated all faces that use a :box attribute to account for the
scenario of a user writing palette overrides that unset the relevant
colour. Thanks to JD Smith for reporting a bug along those lines in
issue 9 of my standard-themes repository (they are derived from the
modus-themes, hence the changes here):
https://github.com/protesilaos/standard-themes/issues/9.
calendar-today and org-date-selected faces are disambiguatedThese two faces are no longer using the same styles. This is because
they can appear in the same buffer. Thanks to Rudolf Adamkovič for
discussing this with me in the context of the same change for my
doric-themes (issue 20 in doric-themes.git):
https://github.com/protesilaos/doric-themes/issues/20.
The Modus concept of “current theme” respects the user’s choice for multiple themes loaded at once. It will return the first Modus theme even if it is not at the front of the list.
[ Emacs will load multiple themes by default, which leads to awkward
colour combinations, unless you know what you are doing—as such all
the Modus commands that load a theme will disable all others, subject
to the user option modus-themes-disable-other-themes. ]
Thanks to Pierre Téchoueyres for reporting the scenario where multiple other themes are loaded on top of a Modus theme. This was done in issue 182: https://github.com/protesilaos/modus-themes/issues/182.
Also thanks to Pierre for covering another snippet that I had missed. This was done in pull request 184: https://github.com/protesilaos/modus-themes/pull/184.
Pierre has assigned copyright to the Free Software Foundation.
There was a typo which caused an error. Thanks to Rudolf Adamkovič for the patch and also for providing a relevant unit test. This was done in pull request 188: https://github.com/protesilaos/modus-themes/pull/188.
Rudolf has assigned copyright to the Free Software Foundation.
Thanks to Basil L. Contovounesios for simplifying a couple of expressions. This was done in pull request 190: https://github.com/protesilaos/modus-themes/pull/190. Basil has assigned copyright to the Free Software Foundation.
The faces of the built-in completion-preview-mode are now
supported. Thanks to Kevin Fleming for asking me about this in issue
178: https://github.com/protesilaos/modus-themes/issues/178.
Several faces that had a strike-through effect when they did not really need it are revised to use a wavy underline instead. The idea is to let the text be readable at all times, regardless of the effective font family. With the strike-through effect, some fonts completely obscure the underlying text.Thanks to Morgan Willcock for discussing with me the use of the strike-through style in issue 169: https://github.com/protesilaos/modus-themes/issues/169.
All symbol-overlay faces are unique, fixing a mistake I had done
before.
The org-dispatcher-highlight, which is used to highlight the keys
of the Org export interface, now uses the appropriate foreground
colour and is always rendered in a bold weight.
The org-habit faces no longer call the function
readable-foreground-color. This is because that function does not
work if the theme is loaded via the early-init.el. Thanks to
Gaston Cabotin for reporting the problem in issue 174:
https://github.com/protesilaos/modus-themes/issues/174.
The gnus-button, which Gnus uses in all sorts of places to mark
some text as clickable, is styled with a less intense underline and
will no longer follow the style of links, including possible palette
overrides. This way, Gnus article buffers will not have visual
noise. Thanks to Morgan Willcock for discussing this with me in
issue 140: https://github.com/protesilaos/modus-themes/issues/140.
.epub and .pdf versions of my free book Emacs Lisp Elements. All files are available in the Git repository: https://github.com/protesilaos/emacs-lisp-elements.
I produced both of those from the source Org file. Here are the steps for posterity:
elispelem.org use the Org export mechanism to generate an Info manual (by default: C-c C-e i i)..texi file, which is then used as the source for the .info file.texi2any --epub elispelem.texi to get the EPUB file.sudo apt install texlive.texi2any --pdf elispelem.texi.The file prompt is used when linking to a file, like with the
denote-link command, or when calling the command
denote-open-or-create (and related).
File names were presented as relative paths without further
modifications. This style relies on the Denote file-naming scheme to
narrow down the list by typing. For example 202512 finds all files
with that in their identifier (i.e. their creation date by default),
_emacs finds all files with the given keyword, and -word with that
word in their title.
The file names are still the same behind the scenes, but their data is
presented in a more readable way. There is no loss of functionality,
meaning that users can still match _keyword, -title, and the like,
per the Denote file-naming scheme.
I might make further changes, though the idea is clear. Expect to get
similar features in my other packages, such as consult-denote,
denote-sequence, denote-journal.
For the denote-sequence package, in particular, the file prompt it
uses shows sequences instead of identifiers as the prefix of each
file. This is because that prompt is relevant for tasks where the user
needs to know the exact sequence, such as when they create a note that
is the child of another.
Power users can study the functions stored in the variable
denote-file-prompt-extra-metadata (same idea for the
denote-sequence package: denote-sequence-file-prompt-extra-metadata).
Denote is a simple note-taking tool for Emacs. It is based on the idea that notes should follow a predictable and descriptive file-naming scheme. The file name must offer a clear indication of what the note is about, without reference to any other metadata. Denote basically streamlines the creation of such files while providing facilities to link between them.
Denote’s file-naming scheme is not limited to “notes”. It can be used for all types of file, including those that are not editable in Emacs, such as videos. Naming files in a consistent way makes their filtering and retrieval considerably easier. Denote provides relevant facilities to rename files, regardless of file type.
denoteM-x
spacious-padding-mode. Adjust the exact spacing values by modifying
the user option spacious-padding-widths.
spacious-paddingThis release introduces some nice refinements and fixes a couple of subtle bugs.
The new user option spacious-padding-subtle-frame-lines supersedes
the ~spacious-padding-subtle-mode-line. It does the same thing,
namely, of making the mode lines use only a thin line instead of a
background. Though it extends this feature to header lines as well.
The documentation string of spacious-padding-subtle-frame-lines
describes the technicalities and includes examples. In short, we can
associate a keyword with either a face that has a foreground color or
a color value directly. For the convenience of the user, the package
also defines the faces spacious-padding-line-active and
spacious-padding-line-inactive. Here is a sample configuration:
;; Read the doc string of `spacious-padding-subtle-mode-line' as it
;; is very flexible. Here we make the mode lines be a single
;; overline, while header lines have an underline.
(setq spacious-padding-subtle-frame-lines
'( :mode-line-active spacious-padding-line-active
:mode-line-inactive spacious-padding-line-inactive
:header-line-active spacious-padding-line-active
:header-line-inactive spacious-padding-line-inactive))
In the future, we might decide that other elements can benefit from this style.
As noted above, when spacious-padding-subtle-frame-lines is
configured to cover header lines, those will be drawn with an
underline. This will not intersect with the text of the header line.
Normally, underlines cut through letters that descend below the
baseline, such as the letters g and y. We choose to avoid that
because it makes for a cleaner interface (though I personally think it
is not a good style for paragraph text, because of the rivers of
negative space it introduces).
This specific design is available for Emacs version 29 or higher.
Users of Emacs 28 must set x-underline-at-descent-line to a
non-nil value. Though note that this has a global effect: we cannot
limit it to a single face.
Thanks to Steven Allen for covering this in pull request 37: https://github.com/protesilaos/spacious-padding/pull/37. Steven has assigned copyright to the Free Software Foundation.
spacious-padding-widths can affect Custom buttonsThis is about the buttons we find in buffers of the Custom system. For
example, we get such a buffer after we do M-x customize.
The relevant keyword is :custom-button-width. It is set to a number
of spaces for padding. Here is the default value and, as always, the
documentation cover the details:
(setq spacious-padding-widths
'( :internal-border-width 15
:header-line-width 4
:mode-line-width 6
:custom-button-width 3 ; the new one
:tab-width 4
:right-divider-width 30
:scroll-bar-width 8
:fringe-width 8))
There were reports about incorrect face specifications that could happen when Emacs was started as a daemon process. I made the relevant changes. Also thanks to kaibagley for covering a line I had missed. This was done in pull request 43: https://github.com/protesilaos/spacious-padding/pull/43.
Related threads:
This is a major rewrite of the book. It has almost doubled in word
count. I explain more concepts and do it from the ground up. Every
single one of the original chapters is redone. They now contain
insights into functionality they would previously hint at, such as how
apply works in practice, what a non-local exit is, and how recursive
editing is done.
I have also added several new chapters. As before, chapters have cross references, so you will benefit from revisiting relevant topics.
If you read the Info version of the book, note that all functions,
variables, and concepts have their own indices. Use those as another
means of navigating the book’s contents. To read the Info version,
clone the Git repository, use M-x dired to open its directory,
move the cursor over the file elispelem.info and do M-x dired-info.
What follows is a brief description of the new chapters.
Basics of how Lisp works: A step-by-step guide to how Lisp code is written and how Emacs can tell apart a function call from a variable even when those have the same symbol.
Introspecting Emacs: Explores some of the ways we can learn about the present state of Emacs. The idea is to develop the habit of asking Emacs about itself before we do an online search. This way we can also expose ourselves to Elisp code, which helps us become better programmers.
Add metadata to symbols: An explanation of how we can associate symbols with data that can then be used for further computations.
What are major and minor modes: Presents the basics of modes in Emacs. It explains what major and minor modes have in common and how they differ, while also discussing relevant concepts of precedence.
Hooks and the advice mechanism: Explains what are hooks and how they work. It also covers the powerful advice mechanism, as some uses of it as similar to what hooks are supposed to do.
Autoloading symbols: Shows how Emacs manages to “lazy load” code when it needs to. This is especially helpful for package developers.
Emacs as a computing environment: A more general view of Emacs that helps us appreciate the value of Emacs Lisp. The more time we put into learning this programming language, the better we will get at controlling large parts of our computing life through Emacs.
pulsarBelow are the release notes.
This version introduces a new feature and makes small refinements to an already reliable base.
In the most common use-case, Pulsar produces a highlight that fades in and out of view after a certain amount of time. The idea with such a “pulse effect” is to quickly get a sense of where the cursor is when some change occurs (e.g. switching to another window).
The permanent static highlights differ from pulse effects in two ways: (i) they do not have a fade-in and fade-out phase and (ii) are not removed automatically. These highlights stick around either until the user removes them or their underlying text is deleted. They are meant to be used as intentional highlights, such as to draw attention to a certain statement while doing a presentation.
The command pulsar-highlight-permanently adds a permanent static
highlight to the current line. When the region is active, the
highlight is applied from the beginning to the end of the region.
The command pulsar-highlight-permanently-remove removes permanent
static highlights from the active region or current line. This command
operates on the entire buffer when it is called with a universal
prefix argument (C-u by default).
The command pulsar-highlight-permanently-dwim adds a permanent
static highlight if there is none or removes it if there is one. It
operates on the currently active region or line at point.
Permanent static highlights are rendered with the face specified in
the user option pulsar-highlight-face.
The commands pulsar-highlight-dwim and pulsar-highlight-line are
obsolete aliases for pulsar-highlight-temporarily.
Temporary static highlights do not have a fade-in and fade-out phase. They are automatically removed as soon as an action occurs. They are an alternative to the aforementioned permanent static highlights.
The command pulsar-highlight-temporarily will operate on the active
region or the current line.
Thanks to Koloszár Gergely for reporting an intermediate bug where the pulse effect actually did not pulse under certain conditions. This was done in issue 31: https://github.com/protesilaos/pulsar/issues/31.
Parts of the code are rewritten in the interest of clarity.
The entire manual is redone to better organise the documentation.
narrow-to-defun), (iii) from point to the end of the buffer, and
(iv) from point to the beginning of the buffer. Variations of these
scopes are also available.
These substitutions are meant to be as quick as possible and, as such,
differ from the standard query-replace (which I still use when
necessary). The provided commands prompt for substitute text and
perform the substitution outright, without moving the point. The
target is the symbol/word at point or the text corresponding to the
currently marked region. All matches in the given scope are
highlighted by default.
substituteBelow are the release notes.
This version iterates on an already stable package. It introduces a few new commands while making minor internal refinements.
The new commands work exactly like those already on offer, except for their scope of application. Remember that Substitute targets either the symbol/word at point or any occurrences of the currently marked characters/text within the given scope and replaces them with the user’s input. The scope is something like “current function definition”, “current paragraph”, “from here to the end of the buffer”, et cetera. The substitution does not move the cursor.
The new commands:
substitute-target-in-outline: Substitute the target across the
current outline level. The outline is determined by the buffer-local
value of the variable outline-regexp. For example, in Org mode the
current outline level includes the heading and any text below it but
not any subheadings.
substitute-target-in-page: Like the above for the boundaries of
the current page. A page is determined by the buffer-local value of
the variable page-delimiter.
substitute-target-in-paragraph: As above for the current
paragraph. Note that in programming modes a “paragraph” is not
necessarily the same as with prose (where, in their simplest form,
paragraphs are delimited by empty lines). Substitute highlights all
matches by default, so users should notice the difference right
away.
substitute-target-in-defun-and-below: This works in the scope of
the current definition (per narrow-to-defun) but only from point
onwards. It is the counterpart to the commands we always had in
substitute-target-in-defun.
The current development target of the Modus themes includes the
function modus-themes-generate-palette. I have updated the manual
with a detailed, step-by-step guide on how to use that function to
derive a custom theme. I am doing it with Solarized, since it is
well-known. Start here: https://protesilaos.com/emacs/modus-themes#h:3a7ede17-f0d4-4322-8e69-1804ed69012b
The reason I wrote this function is to make it easier for users to get started with their Modus ports. The common workflow is for someone to copy a colour scheme from their favourite terminal emulator and use it as a starting point for their Emacs theme. Modus will do the heavy lifting in the background.
modus-themes-generate-palette returns a fully fledged Modus palette:
the literal hundreds of entries are all defined, even if we only give
it a couple of colour values. The more colours we specify, the more
defined the character of the resulting palette will be—and we have
complete control, as the manual demonstrates.
A good starting point is to provide values for the main background (bg-main),
main foreground (fg-main), and the basic hues of red, green,
yellow, blue, magenta, and cyan.
Options for power users are available. Though the point is to lower the barrier to entry for those who want to start with something tolerable. Then they can trust that I cover everything they need to gradually evolve their theme to be as complete as core Modus.
In the manual I used Solarized as an example, with usable code snippets that you can copy+paste. Those yield a theme, if you try them. Below is a complete, albeit generic, Modus+Solarized theme. It is a decent starting point.
;; Modus+Solarized dark
(defvar modus-solarized-dark-palette
(modus-themes-generate-palette
'((bg-main "#073642")
(fg-main "#EEE8D5")
(red "#DC322F")
(green "#859900")
(yellow "#B58900")
(blue "#268BD2")
(magenta "#D33682")
(cyan "#2AA198"))))
(modus-themes-theme
'modus-solarized-dark
'modus-solarized-themes
"Sample of a basic Solarized dark port."
'dark
'modus-solarized-dark-palette
nil
nil)
Do not rush to play around with this snippet. The chapter in the manual I linked to above has much more about this topic. Take some time to read through it. You can extend the port to also map out all the colors you want. Thus:
;; Modus+Solarized dark
(defvar modus-solarized-dark-palette
(modus-themes-generate-palette
;; We provide the two base colors of Solarized, plus most of its
;; accents. These form the BASE-COLORS we pass as an argument.
;; All other color values come from those. The BASE-COLORS here
;; are enough to generate a new palatte that has no traces of, say,
;; the `modus-vivendi' color values.
'((bg-main "#073642")
(fg-main "#EEE8D5")
(red "#DC322F")
(green "#859900")
(yellow "#B58900")
(blue "#268BD2")
(magenta "#D33682")
(cyan "#2AA198"))
;; The COOL-OR-WARM-PREFERENCE is derived internally based on
;; `bg-main'. We can pass it here if we feel strongly about it.
nil
;; If we need to specify the CORE-PALETTE from where to inherit any
;; missing colors and/or semantic mappings, we can give it here.
;; Though nil is the appropriate starting point, as the code will
;; handle things internally.
nil
;; And here are our MAPPINGS where we can specify what values apply
;; to which semantic color. The `modus-themes-list-colors' shows
;; them all.
;;
;; Note that in our BASE-COLORS above we never wrote what, say,
;; `magenta-warmer' is: it is derived programmatically from the
;; `magenta' we have there. Absent that, it would be taken from
;; the CORE-PALETTE.
'((cursor magenta-warmer)
(bg-hl-line bg-blue-nuanced)
(bg-paren-match bg-magenta-subtle)
(bg-region bg-blue-intense)
(fg-region fg-dim)
(bg-mode-line-active bg-blue-nuanced)
(fg-mode-line-active blue-warmer)
(border-mode-line-active blue-cooler))))
(modus-themes-theme
'modus-solarized-dark
'modus-solarized-themes
"Sample of a basic Solarized dark port."
'dark
'modus-solarized-dark-palette
nil
nil)
Using this infrastructure, I am confident that we can implement any colour scheme as a Modus theme—and, again, we can configure every part of it.
The manual is extensive, though remember that I remain at your disposal in case something is unclear: open an issue in the Modus themes repository or contact me and I will help you.
modus-themes (also built into Emacs)If you want maximalist themes in terms of colour, check my ef-themes
package. For something in-between, which I would consider the best
“default theme” for a text editor, opt for my modus-themes.
doric-themesBelow are the release notes.
These are doric-siren and doric-mermaid. The former is a light
theme while the latter is dark. Both themes offer a marine vibe
combined with warm hues. Check the updated screenshots of all the
Doric themes: https://protesilaos.com/emacs/doric-themes-pictures.
Those are used when the user option transient-semantic-coloring is
set to a non-nil value (the default). They are designed to indicate
different types of key, such as those that keep the transient in place
as opposed to exiting it.
The old style of making all key bindings look the same is still
available if transient-semantic-coloring is set to nil.
This is a new feature coming in Emacs 31. Users can opt in to it by
setting the user option elisp-fontify-semantically to a non-nil
value. When active, Emacs Lisp buffers will apply highlights to more
elements of the syntax. Those will still conform with the minimalist
style of the Doric themes, but users at least get the benefits of
elisp-add-help-echo.
Those are typically applied by major modes that are power by tree-sitter. Again, the styles are minimalist.
These include:
ert-test-result-unexpected and ert-test-result-expected,
courtesy of Rudolf Adamkovič in pull request 19:
https://github.com/protesilaos/doric-themes/pull/19.The calendar-today and org-date-selected are similar but
different enough to stand apart. I initially made them look identical
because I thought they would never appear in the same context. Thanks
to Rudolf Adamkovič for telling me they could actually be collocated,
hence their disambiguation. This was done in issue 20:
https://github.com/protesilaos/doric-themes/issues/20.
The transient-argument and transient-value have a background to
make them easier to spot. This is because they convey information
about the state of the transient interface, which typically is important.
Transient’s mismatching keys are dimmed out instead of being underlined, because this looks better in context.
Similarly, org-agenda-clocking has a more pronounced background.
Idem for eldoc-highlight-function-argument and its counterparts in
lsp-mode and geiser.
The Org check boxes are rendered with a bold weight, while quote blocks are in italics. Thanks to Rudolf Adamkovič for the patch in pull request 18: https://github.com/protesilaos/doric-themes/pull/18.
All Notmuch cryptography-related faces have more refined styles to both stand out better while avoiding exaggerations.
The Custom user interface buttons inherit from variable-pitch.
The current line number in display-line-numbers-mode uses just a
bold weight to avoid exaggerations.
denote-merge package is an optional extension to denote. I am
providing it in response to requests for functionality that
streamlines the work of merging contents from one file into another.
Thanks to Sia Piperea for reminding me about it, making a suggestion
for region-related functions, and for testing my prototype:
The denote-merge package is a more refined implementation of those
ideas, plus more. As of now, the package provides support for two
broad kinds of “merging”:
For merging a region, in particular, there are options and concomitant convenience commands to format the region in a certain way, such as an Org source block, a Markdown blockquote, a plain indented region, and the like.
The official manual describes the technicalities. In short, I have taken care to make the relevant commands do the right thing, including to update backlinks, annotate the contents, and establish links, where relevant.
Note that the GNU ELPA package is not available yet. I have prepared the relevant patch and am expecting everything to be done in the near future.
Please stay tuned.
denote-merge (Coming soon!!!)doric-themes
package for Emacs. These combine marine hues with some warmer accents,
cast on a fairly prominent backdrop. doric-siren is a light theme,
while doric-mermaid is dark. Below are some screenshots to give you
an idea of what to expect.
[ Or just check all the pictures: https://protesilaos.com/emacs/doric-themes-pictures. ]
doric-themes version 0.5.0The character of the themes is established and, in principle, I will
not change them further. Though I am giving it a few more days of
further testing to confirm I did not miss something. Expect these and
other improvements to be available in the next stable version of the
doric-themes, which I expect to publish before the end of November.
The Doric themes use few colours and will appear monochromatic in many contexts. They are my most minimalist themes. Styles involve the careful use of typographic features and subtleties in colour gradients to establish a consistent rhythm. Legibility is still high.
If you want maximalist themes in terms of colour, check my ef-themes
package. For something in-between, which I would consider the best
“default theme” for a text editor, opt for my modus-themes.
doric-themesmodus-themes with complete
step-by-step examples on how to define a derivative theme. Here is the
commit:
commit c94938ff794d043447ed4ffe9b55f1031039f667
Author: Protesilaos Stavrou <[email protected]>
Date: Mon Nov 10 13:02:49 2025 +0200
Greatly expand the manual with complete examples of creating Modus derivatives
This covers the basic and more advanced use cases. It also is for both
private use and making new packages.
doc/modus-themes.info | 588 +++++++++++++++++++++++++++++++++++++-------------
doc/modus-themes.org | 278 +++++++++++++++++++++++-
2 files changed, 711 insertions(+), 155 deletions(-)
The relevant sections and their structure:
If something is unclear, you are welcome to contact me.
]]>standard-themes are a collection of light and dark themes for
GNU Emacs. The standard-light and standard-dark emulate the
out-of-the-box looks of Emacs (which technically do NOT constitute a
theme) while bringing to them thematic consistency, customizability,
and extensibility. Other themes are stylistic variations of those.
In practice, the Standard themes take the default style of the font-lock and Org faces, complement it with a wider and harmonious colour palette, address many inconsistencies, and apply established semantic patterns across all interfaces by supporting a large number of packages.
standard-themesBelow are the release notes.
This major version makes Standard build on top of my Modus themes. The latter provides extensive face/package coverage and a wide range of customisation options. As a result, the Standard themes retain their design while giving more control to the user.
These release notes are essentially the same as what I wrote earlier
today for my ef-themes package, which is now also built on top of my
Modus themes: https://protesilaos.com/codelog/2025-11-09-emacs-ef-themes-2-0-0/.
In short:
standard-themes-take-over-modus-themes-mode can
be enabled to make all Modus commands that load a theme only
consider the Standard themes. This is effectively the opposite of
enabling the modus-themes-include-derivatives-mode or using one of
the Standard commands to load just a Standard theme (like
standard-themes-rotate).(info "(modus-themes) Top") or visit https://protesilaos.com/emacs/modus-themes.You are welcome to contact me if something is unclear.
]]>ef-themes are a collection of light and dark themes for GNU
Emacs that provide colourful (“pretty”) yet legible options for users
who want something with a bit more flair than the modus-themes (also
designed by me).
ef-themesBelow are the release notes.
The Ef themes are now derived from my Modus themes. This means that they inherit the wide face coverage and extensive customisability of Modus, while retaining their stylistic flair.
The Ef themes no longer provide any user options of their own. Each of the options we had before is now an alias for the Modus equivalent. As part of this transition, the Ef themes actually gain new customisation options, which are documented herein.
Furthermore, the Ef themes do not define any commands to load a theme. What we had before is once again an alias for the equivalent Modus command.
The manual of the Ef themes describes these compatibility arrangements. Further documentation and code samples are available in the manual of the Modus themes:
(info "(modus-themes) Top").| Old name | Is alias for CURRENT NAME |
|---|---|
| ef-themes-disable-other-themes | modus-themes-disable-other-themes |
| ef-themes-to-toggle | modus-themes-to-toggle |
| ef-themes-to-rotate | modus-themes-to-rotate |
| ef-themes-italic-constructs | modus-themes-italic-constructs |
| ef-themes-bold-constructs | modus-themes-bold-constructs |
| ef-themes-variable-pitch-ui | modus-themes-variable-pitch-ui |
| ef-themes-mixed-fonts | modus-themes-mixed-fonts |
| ef-themes-headings | modus-themes-headings |
| ef-themes-completions | modus-themes-completions |
| ef-themes-prompts | modus-themes-prompts |
| ef-themes-common-palette-overrides | modus-themes-common-palette-overrides |
| ef-themes-post-load-hook | modus-themes-after-load-theme-hook |
| ef-themes-after-load-theme-hook | modus-themes-after-load-theme-hook |
Please read their respective documentation strings.
If you use Ef and possibly other Modus derivatives, you may prefer to switch all your user options to the Modus ones. This way you can keep a unified configuration for all your themes.
In the past, the Ef themes did not provide an option to disable the
extensive use of a bold font weight and italic font slant. Whereas now
those are controlled by the user options modus-themes-bold-constructs
and modus-themes-italic-constructs. By default, when they are nil,
bold and italics are used only when necessary. Set these user options
to t to have bold and italics in more places.
All the old commands Ef provided for loading one of its themes will still work as before, meaning that they will only ever show and load a theme that belongs to the Ef collection.
Internally, these commands are now using the Modus infrastructure and are then limiting the set of themes to the Ef collection. They are thus convenience wrappers around the equivalent Modus commands.
ef-themes-toggleef-themes-rotateef-themes-selectef-themes-load-randomef-themes-load-random-darkef-themes-load-random-lightAdditionally, the commands ef-themes-list-colors and
ef-themes-list-colors-current use the relevant Modus functionality
under the hood while working only with the Ef themes.
The minor mode modus-themes-include-derivatives-mode makes all the
theme-loading commands that Modus defines consider all core and
derivative themes. This means that something like modus-themes-select
will offer modus-operandi and ef-summer as two of the many
possible candidates.
In this scenario, the Modus and Ef collections become part of a larger
family of themes as opposed to only considering one or the other
(Loading only Ef themes with the convenience wrappers we provide).
Enable modus-themes-include-derivatives-mode and then access them
all with the following commands:
modus-themes-togglemodus-themes-rotatemodus-themes-selectmodus-themes-load-randommodus-themes-load-random-darkmodus-themes-load-random-lightmodus-themes-list-colorsmodus-themes-list-colors-currentConsult the manual of the Modus themes for further details and/or read the documentation string of each command.
The minor mode ef-themes-take-over-modus-themes-mode makes all Modus
commands that load a theme consider only Ef themes. This is the
opposite of allowing different theme collections to be blended
together (Combining core Modus themes with all their derivatives).
It effectively is the same as using one of the convenience wrapper
commands we already provide (Loading only Ef themes with the convenience wrappers we provide).
This minor mode exists to accommodate the needs of users who install
both the Modus and Ef theme packages (and possibly other Modus
derivatives). They can maintain a single/shared configuration and then
decide when to enable ef-themes-take-over-modus-themes-mode to
switch to using just the Ef themes.
For example, in the morning the user only wants to consider the Modus
themes, but during the evening they only likee the Ef themes. In this
scenario, a key binding to, say, modus-themes-rotate does not need
to be updated: it internally reads only Ef themes when
ef-themes-take-over-modus-themes-mode is enable. When this minor
mode is disabled, that same key binding will revert to doing what it
did before (load only a core Modus theme or Modus plus derivatives if
modus-themes-include-derivatives-mode is enabled).
Modus has always had a more comprehensive palette than Ef. Now they are on par. Concretely, this means that users can customise more of the theme’s style to their liking. Use the commands that list/preview a palette to learn about all the available options.
The manual of the Modus themes covers the palette and user-defined overrides at great length. It also includes code samples that you can use as a starting point. And if something is unclear, you are welcome to contact me.
The wide face/package coverage of Modus is now inherited by the Ef themes. If some interface you are using does not look right, then the underlying faces are probably not supported yet. Contact me and I will take care of it right away.
]]>modus-themes (also built into Emacs 28+)This version fixes a critical bug in the modus-themes-with-colors
macro. In short, it was not working as before or was not working at
all. Now it should do the right thing.
Thanks to Alexandr Semenov for reporting a relevant bug in issue 170 and for helping me test the results: https://github.com/protesilaos/modus-themes/issues/170.
Thanks to Stéphane Marks for testing some configurations with the latest stable Emacs version as well as with builds from emacs.git. This was done via a private channel and I am sharing this information with permission.
Also thanks to Stefan Monnier for monitoring my commits as I was
trying to refactor the modus-themes-with-colors macro. Some comments
were posted on the emacs-devel mailing list, as well as a patch that I
ended up applying and then reverting (check the commit log for the
technicalities): https://lists.gnu.org/archive/html/emacs-devel/2025-11/msg00114.html.
Apologies to everyone for the inconvenience! This was a tricky bug. The good thing is that it compelled me to improve several parts of the code.
This version also includes a clarification in the manual about building a theme on top of Modus:
(info "(modus-themes) Build on top of the Modus themes").In short, it mentions that a theme exists in an appropriately named
file that is part of the custom-theme-load-path. Thanks to Ashton
Wiersdorf for asking a related question in issue 171:
https://github.com/protesilaos/modus-themes/issues/171.
modus-themes (also built into Emacs 28+)This is a major release. There are some small breaking changes. The
big new feature is that Modus can be used as the basis for other theme
projects. Two of my other theme packages, the ef-themes and the
standard-themes are already done in this way: their next major
versions will formalise what I have been developing for a while now
(though the doric-themes will remain their own thing for the time
being).
This is of immediate interest to package developers or advanced users. It changes nothing for existing users of the Modus themes. Please refer to the manual on the matter and feel welcome to contact me if you have any questions—I am happy to help.
Evaluate:
(info "(modus-themes) Build on top of the Modus themes")
Or visit: https://protesilaos.com/emacs/modus-themes#h:86eb375b-9be4-43ce-879a-0686a524a63b.
Why build on top of Modus? To benefit from (i) the wide face coverage
and extensive detail-oriented testing, (ii) the use of palette
mappings, (iii) the palette preview done with
modus-themes-list-colors or related, (iv) the inclusion of the
derivative theme in the workings of all Modus commands that load a
theme, like modus-themes-rotate and modus-themes-select (see the
new minor mode modus-themes-include-derivatives-mode).
Derivative themes can be as simple as a few extra colours on top of,
say, modus-operandi. They can also be more involved, with new
palette mappings and custom faces that use them. Such themes can
define commands which load only their own themes (see
modus-themes-define-derivative-command) and they can even take over
the Modus themes completely, in terms of becoming the only ones that
are exposed to the commands that load a theme, like
modus-themes-rotate and modus-themes-select.
Thanks to Suleyman Boyar for fixing an intermediate omission with an earlier version of the code that derives a theme. I did eventually change the whole approach to use a function instead of a macro, but Suleyman’s contribution was still useful at the time. This was done in pull request 156: https://github.com/protesilaos/modus-themes/pull/156. The change is small, meaning that Suleyman does not need to assign copyright to the Free Software Foundation.
The commands modus-themes-list-colors and modus-themes-list-colors-current
display the given theme’s palette in a tabulated listing. Each line
visualises the colour it references. From that buffer it is now
possible to call the following commands (bound to the modus-themes-preview-mode-map):
| Default key binding | Name of the command |
|---|---|
w |
modus-themes-preview-mode-copy-color |
W |
modus-themes-preview-mode-copy-entry |
m |
modus-themes-preview-mode-mark |
M |
modus-themes-preview-mode-mark-all |
u |
modus-themes-preview-mode-unmark |
U |
modus-themes-preview-mode-unmark-all |
The modus-themes-preview-mode-copy-color and its
modus-themes-preview-mode-copy-entry counterpart work on the line at
point or those that are marked. The first copies the resolved colour
value, as a string (“resolved” here means that it will find what a
palette mapping actually stands for). The second gets the complete
palette entry, as it appears in the underlying definition. This is a
starting point for writing palette overrides.
Thanks to shimeike for reporting a problem with a regression I introduced by mistake in one of the development versions of the function that handles the creation of the tabulated list. This was done in issue 163: https://github.com/protesilaos/modus-themes/issues/163.
The fnname-call and variable-use are now available. They are
applied to faces that are used by major modes which build on top of
tree-sitter program as well as the Emacs Lisp semantic highlighting
feature of Emacs 31.
By default, these colours are more subtle than their fnname and
variable counterparts. The reason is that I want to avoid
exaggerations with the wanton application of colour (“avoiding
exaggerations” is one of the design principles of the Modus themes).
Because of the commitment to the highest legibility standard for colour contrast, we are using colours that stand out in their context, yet are still harmonious with each other. This is due to (i) careful consideration of colour harmonies, (ii) enforced uniformity of styles where necessary to avoid emphasising the emphasis or stating the obvious, and (ii) the fact that not too many intense colours appear in close proximity to each other in most Emacs major modes. If a major mode or other feature adds colours virtually everywhere, then the Modus themes are no longer true to their character and become borderline unusable.
The more subtle colours for fnname-call and variable-use do not
prevent exaggerations, though they do make them less egregious.
[ Note that themes do not control which face is applied where. That is the job of a major mode or specialised minor modes. When in doubt, work with the rule that fewer colours are better than many. ]
This is built into Emacs version 31. What it does is make different
parts of the Elisp syntax stand out visually. It also augments them
with a mouse tooltip (technically a help-echo) that provides
valuable insights about what the thing is.
Thanks to Eshel Yaron, the author of this feature, for (i) asking me
to review the relevant faces, (ii) helping me understand what
“semantic highlighting” is intended for, and (iii) explaining to me
how some faces are meant to be used. This was done in the context of
Emacs bug 79677 and in an aside about a change to my minimalist
doric-themes package:
I wrote to Eshel that I do not want many colours clustered together. This design is more suitable to themes with low intensity. All my themes exacerbate the effect of dense colouration due to their primary design goal: legibility.
Currently the added colouration of semantic highlighting is noticeable
far more than I like. This is a compromise on my end. Future versions
of the themes might set more semantic faces to nil (or anyhow make
them subtle), subject to further review and user feedback.
This is not a position against semantic highlighting, but a comment on inconsiderate themes.
I have revised all supported faces to make them more conservative
about when they apply typographic qualities that deviate from the
default font. Many faces that were unconditionally rendered with a
bold weight or an italic slant are now subject to the user options
modus-themes-bold-constructs and modus-themes-italic-constructs.
This means that they are like the default font family by default and
will become bold/italic if the relevant user option is set to a
non-nil value.
Faces that are semantically bold or italic and/or which genuinely benefit from such typographic attributes out-of-the-box are exempt from this.
Headings are also exempt because they are anyway subject to the user
option modus-themes-headings which covers the height and weight of
each heading level’s text.
Thanks to chainedghost for reporting a regression in one of the commits I made. This was done in issue 167: https://github.com/protesilaos/modus-themes/issues/167
lsp-mode and lsp-ui packagesThanks to Jimmy Yuen Ho Wong for the initial contribution in pull request 142: https://github.com/protesilaos/modus-themes/pull/142. I made some changes on top to use semantic palette mappings, where applicable, and other such tweaks.
minibuffer-nonselected face is supportedThis is coming in Emacs version 31: it highlights the active minibuffer when the cursor is in another window. The face is designed to be as noticeable as possible because this kind of scenario usually leads to mistakes or confusion.
The markdown-code-face uses the same semantic palette mapping as
org-block. This was always the intent. Thanks to Frédéric Giquel
for the fix in pull request 160: https://github.com/protesilaos/modus-themes/pull/160.
The change is small and thus does not require copyright assignment
to the Free Software Foundation.
Fixed a word in the symbol of the flymake-warning-echo-at-eol. Thanks to
Eric Ottosson for the patch in pull request 145: https://github.com/protesilaos/modus-themes/pull/145.
No copyright assignment is required.
The two new Emacs 31 faces for packages that are marked for
installation or deletion in the M-x list-packages buffer are now
included. Thanks to Gautier Ponsinet for sending me the patch.
Gautier has assigned copyright to the Free Software Foundation.
The hs-ellipsis in Emacs 31 from the built-in hideshow package
is also covered. It is made to conform with established patterns
across conceptually equivalent faces (e.g. Org folded headings)
rather than do its own thing.
The widget-field uses an underline to be easier to spot. On
supported Emacs displays (generally graphical Emacs) the underline
is at the descent line, meaning that it does not cross through
characters with descenders like g.
The git-commit and log-edit faces for writing commit messages in
Magit and VC are tweaked to avoid exaggerations.
The Magit section headings have a slightly modified foreground value.
The org-list-dt face for list definition terms has a slightly
modified foreground value to (i) be easier to spot while (ii) being
less intense overall.
The calendar-today in the M-x calendar interface now has a box
around it on supported displays (graphical Emacs) to better stand
out from its context. On unsupported displays, the face still gets
an appropriate background value.
The org-date-selected and calendar-today should look the same,
contrary to how they did before. This is done in the interest of
theme-wide consistency (i.e. the kind of detail you appreciate once
it is no longer there).
The mode lines use an underline on displays that cannot render a box. This way, the faces remain distinct from their context.
All interface buttons, like those that appear in the Custom buffers, will be rendered with an underline on displays that cannot handle boxes.
The notmuch-message-summary-face, which is used in threaded
message views, now has an overline on supported displays (graphical
Emacs). Individual messages in the thread should thus be more
discernible.
The tab-bar-tab-highlight is directly covered to make sure all
mouse hover effects are consistent across similar interfaces.
Some faces that had specific colour values (e.g. cyan-cooler) are
revised to use semantic palette mappings instead. This ensures
thematic consistency. The most obvious beneficiaries are users of
the deuteranopia- and tritanopia- optimised themes
(deuteranopia-friendly themes cannot work with red and green while
tritanopia-friendly themes must not rely on yellow and blue).
modus-themes-custom-auto-reload is obsoletePlus all our user options no longer specify a :set function. This
means that the theme will never be reloaded automatically when using
the Custom interface or setopt to configure one of the theme’s user
options. Reloading the theme is what applies the new styles. The idea
of this feature is good, but the implementation was problematic. Maybe
I will reinstate it in the future, though I cannot let it block this
major release.
modus-themes-* faces are obsoleteIn previous versions I was defining faces that were used to apply
uniform styles across many packages. This was the old way of doing
things before version 4.0.0 of the themes that introduced palettes
with semantic mappings and, optionally, user-defined overrides to
them.
I have deprecated all of the following and made all the necessary changes (i.e. several thousands of them), to make palette mappings the norm everywhere. In general, I encourage you not to rely on any faces defined by Modus: they are really intended for internal use.
modus-themes-fg-bluemodus-themes-fg-blue-coolermodus-themes-fg-blue-faintmodus-themes-fg-blue-intensemodus-themes-fg-blue-warmermodus-themes-fg-cyanmodus-themes-fg-cyan-coolermodus-themes-fg-cyan-faintmodus-themes-fg-cyan-intensemodus-themes-fg-cyan-warmermodus-themes-fg-greenmodus-themes-fg-green-coolermodus-themes-fg-green-faintmodus-themes-fg-green-intensemodus-themes-fg-green-warmermodus-themes-fg-magentamodus-themes-fg-magenta-coolermodus-themes-fg-magenta-faintmodus-themes-fg-magenta-intensemodus-themes-fg-magenta-warmermodus-themes-fg-redmodus-themes-fg-red-coolermodus-themes-fg-red-faintmodus-themes-fg-red-intensemodus-themes-fg-red-warmermodus-themes-fg-yellowmodus-themes-fg-yellow-coolermodus-themes-fg-yellow-faintmodus-themes-fg-yellow-intensemodus-themes-fg-yellow-warmermodus-themes-intense-bluemodus-themes-intense-cyanmodus-themes-intense-greenmodus-themes-intense-magentamodus-themes-intense-redmodus-themes-intense-yellowmodus-themes-key-bindingmodus-themes-lang-errormodus-themes-lang-notemodus-themes-lang-warningmodus-themes-mark-altmodus-themes-mark-delmodus-themes-mark-selmodus-themes-nuanced-bluemodus-themes-nuanced-cyanmodus-themes-nuanced-greenmodus-themes-nuanced-magentamodus-themes-nuanced-redmodus-themes-nuanced-yellowmodus-themes-prominent-errormodus-themes-prominent-notemodus-themes-prominent-warningmodus-themes-prose-codemodus-themes-prose-macromodus-themes-prose-verbatimmodus-themes-search-currentmodus-themes-search-lazymodus-themes-search-replacemodus-themes-search-rx-group-0modus-themes-search-rx-group-1modus-themes-search-rx-group-2modus-themes-search-rx-group-3modus-themes-search-staticmodus-themes-subtle-bluemodus-themes-subtle-cyanmodus-themes-subtle-greenmodus-themes-subtle-magentamodus-themes-subtle-redmodus-themes-subtle-yellowThanks to Kevin Fleming and Jimmy Yuen Ho Wong for fixing two cases of unbalanced/faulty parentheses caused by the aforementioned. These were done as pull requests 166 and 168, respectively:
Both changes are small: no copyright assignment to the Free Software Foundation is needed (and I think both authors are covered, anyway).
:inherit face attributes are reviewed and kept to a minimumThe themes now use the :inherit attribute much more cautiously than
before. This is because of the reliance on semantic palette mappings:
those give us more control and usually are explicit about the desired
result.
Face inheritance can become circular in some cases. Newer Emacs versions throw an error accordingly. This was discussed in Emacs bug 79672, with participation from Eli Zaretskii, JD Smith, Manuel Giraud, Stephane Marks, and Steven Allen: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=79672.
Thanks to everybody involved!
]]>Denote is based on the idea that files should follow a predictable and descriptive file-naming scheme. The file name must offer a clear indication of what the contents are about, without reference to any other metadata. Denote basically streamlines the creation of such files or file names while providing facilities to link between them (where those files are editable).
Denote’s file-naming scheme is not limited to “notes”. It can be used for all types of file, including those that are not editable in Emacs, such as videos. Naming files in a constistent way makes their filtering and retrieval considerably easier. Denote provides relevant facilities to rename files, regardless of file type.
denoteBelow are the release notes.
The overarching theme of version 4.1.0 is that of continuing to
deliver on the highly hackable/flexible/powerful potential of Denote.
All the core functionality is the same as before and you still only
need a tiny configuration to use Denote productively. We are adding
more refinements and subtle improvements under the hood, while making
it even easier for advanced users/developers to piece together a
workflow that matches their particular needs.
Since version 4.0.0, we have moved the “Denote extras” files into
their own packages. I cover their changes further down after I
elaborate on all the changes to the core Denote package:
Note that I write all the release notes by hand. The reason is that I consider this process an essential part of my role as a maintainer. Taking the time to review and document everything ensures that (i) I am well informed about the state of the package, (ii) I did not forget anything about this development cycle, and (iii) I take another look at the changes we made to ensure everything is in order.
The user option denote-directory can be assigned to a list of
directories. The original string value is also acceptable and
remains the default.
Advanced users can define a completely custom scheme for identifiers. This can be as simple as automatically assigned ordinal numbers to increasingly complex patterns that may also involve user input.
The command denote-find-backlink-with-location is like
denote-find-backlink except it also moves to the exact location of
the link in the corresponding file.
All Denote buffer names share a common prefix, which is subject to
the user option denote-buffer-name-prefix. This makes it easier to
spot them in the buffer list.
The command denote-dired (alias denote-sort-dired) can sort
files by random and last-modified in addition to the methods
that involve Denote file name components.
The user option denote-query-sorting controls how files in all
query buffers are sorted by default. This covers backlinks, query
links for file contents, and any buffer produced by the
denote-grep command. It benefits from the internal “Denote sort”
mechanism, which is also used by denote-dired and the Org dynamic
blocks of the denote-org package.
Remember that the release notes are true only at the time of publication. The single source of truth always is the official manual.
denote-directory to a list of directoriesThe user option denote-directory can optionally be bound to a list
of file system paths, each of which represents a directory root, such
as '("/home/prot/Documents/work/" "=/home/prot/Git/hut/") (Denote
has always supported subdirectories, even for a singular denote-directory).
When creating new files, such as with the denote command, the first
directory on the list will be selected. This can be changed by
modifying the denote-prompts user option so that it asks for a
directory or subdirectory thereof. Or by writing small wrapper
commands that put files in a predefined directory, like this:
(defun my-denote-for-work ()
"Like `denote' but always use the ~/Documents/work/ directory."
(interactive)
(let ((denote-use-directory "~/Documents/work/"))
(call-interactively 'denote)))
Why have many directories as part of the denote-directory? Some
users want to maintain separate directories with Denote files, while
retaining the option of establishing links between (unlike the concept
of “silos” we support, where the directories are self-contained).
Those two directories can be their own Git repositories, for example,
and have different syncing policies for access across multiple
devices.
Thanks to Jean-Philippe Gagné Guay for providing the core functionality in pull request 609: https://github.com/protesilaos/denote/pull/609.
Jean-Philippe is a long-time contributor who has assigned copyright to
the Free Software Foundation. I made several changes on top, such as
to allow denote-dired (alias denote-sort-dired) to work with many
directories, by finding their common root (which would ultimately be
/).
The default Denote identifier should work for most people in most cases. I have considered the Denote file-naming scheme carefully and know it is reliable. Advanced users who have a clear use-case of something out-of-the-ordinary can now get “Denoted” file names with arbitrary identifiers. Since this is an advanced feature, I will not elaborate here on the technicalities. Though I have taken the time to write at length in the manual about it, with concrete examples ranging from simple to more complex scenaria. Start with this introduction: https://protesilaos.com/emacs/denote#h:3048f558-7d84-45d6-9ef2-53055483e801.
This feature has been discussed and requested for a long time, across several related issues:
Thanks, in no particular order, to mentalisttraceur, juh2, Christian Tietze, and Jean-Philippe Gagné Guay for sharing their thoughts. Also thanks to Jean-Philippe Gagné Guay for contributing the patches that made this possible, the last of which is in pull request 586: https://github.com/protesilaos/denote/issues/586.
Denote has two ways of working with backlinks: (i) to display them in
a dedicated buffer and (ii) to use a minibuffer prompt that supports
completion in order to pick one file out of the list. The new command
denote-find-backlink-with-location is of the latter kind. Like its
more generic denote-find-backlink counterpart, it uses the
minibuffer to pick a file that links to the current one. Then, it also
moves the cursor to where the link is.
I did this is in response to issue 471 as reported by johkneisl: https://github.com/protesilaos/denote/issues/471.
denote-buffer-name-prefixThis is a new user option that takes an arbitrary string. Its value is
"[D]" by default. It consolidates how we name all of our buffers.
Out-of-the-box, this applies to special buffers, like those produced
by the commands denote-backlinks and denote-dired. When the
optional minor mode denote-rename-buffer-mode is enabled, all
buffers whose file naming scheme is that of Denote will get the
denote-buffer-name-prefix in addition to their own denote-rename-buffer-format.
This is related to pull request 597 by James Kalyan: https://github.com/protesilaos/denote/pull/597. James has assigned copyright to the Free Software Foundation.
random and last-modifiedIn core Denote, this is used internally by the denote-dired command
(alias denote-sort-dired) and the query buffers (more in the next
section). By default, denote-dired prompts for the sort method and
whether to reverse the order. Though there are user options to tweak
its behaviour (consult the manual). The Org dynamic blocks of the
denote-org package also rely on this mechanism (denote-org version 0.2.0).
Query buffers are those of denote-backlinks, denote-grep, and
denote-query-contents-link (in the latter case, the query buffer is
produced when you click on the link). Users can now modify how they
sort matching files via the option denote-query-sorting. The sorting
methods are by any of the Denote file names components (per
denote-sort-components), random order, last modified, or an
arbitrary function.
Thanks to Lucas Quintana for the contribution in pull request 594: https://github.com/protesilaos/denote/pull/594. Lucas has assigned copyright to the Free Software Foundation.
The denote-dired command (alias denote-sort-dired) sets up its
revert-buffer-function in a more robust way, such that it does not
affect the window layout under certain conditions.
Fixed a bug with the menu entry of Denote that broke
context-menu-mode. It addresses issue 592, as reported by artelse:
https://github.com/protesilaos/denote/issues/592. Thanks to Lucas
Quintana for the patch. Lucas has assigned copyright to the Free
Software Foundation.
denote-use-template and its potential interference with
denote-org-capture-identifiers in custom user code. We discussed
this in issue 545 and the patch was sent as pull request 598:
An internal check to determine if a file has backlinks is now faster
than before: it exits with a non-nil value as soon as it finds one
match, instead of trying to collect all the backlinks. Thanks to
Yann Dutrieux for addressing an omission of mine in the original
implementation and then for discussing with me some further
refinements. Yann’s contribution was sent as pull request 637:
https://github.com/protesilaos/denote/pull/637.
~/Git/Projects/denote $ git shortlog 4.0.0..4.1.0 --summary --numbered
156 Protesilaos Stavrou
29 Jean-Philippe Gagné Guay
4 Lucas Quintana
2 James Kalyan
1 Alan Schmitt
1 Ashish Panigrahi
1 Hanwen Guo
1 Ryota
1 Yann Dutrieux
This concerns all the former “Denote extras” that were shipped with
core Denote as well as consult-denote.
consult-denote version 0.4.0The commands consult-denote-grep and consult-denote-find also
work when denote-directory is set to a list value, as explained
further above (Set denote-directory to a list of directories).
There is a Consult-powered counterpart to the
denote-sequence-file-prompt. It is for users of the
denote-sequence package and comes into effect when the
consult-denote-mode is enabled (that mode “Consults” all relevant
minibuffer prompts Denote uses so they get the familiar preview
functionality). The denote-sequence-file-prompt is used by
commands such as denote-sequence, denote-sequence-find, and
denote-sequence-link, among others (denote-sequence version 0.2.0).
The command consult-denote-find sorts its files by default. We
discussed this with Gianluca Della Vedova and Gianluca implemented
the tweak in pull request 18:
https://github.com/protesilaos/consult-denote/pull/18. The change
is small, meaning that Gianluca does not need to assign copyright to
the Free Software Foundation.
denote-journal version 0.2.0denote-journal-new-or-existing-entry to determine what
“new” and “existing” mean. Users may want to, for example, maintain
one file per month, with daily entries as headings or as free-form
text. The user option denote-journal-interval specifies the
interval as a symbol among daily, weekly, monthly, and
yearly. Thanks to Ning Xu for floating the idea of non-daily
journaling in issue 18: https://github.com/protesilaos/denote-journal/issues/18.
The new user option denote-journal-signature specifies a signature
that will be applied to all new journal entries. It is the
counterpart of denote-journal-keyword. Possible values are nil,
for no predefined signature, a string for a constant signature, and a
function that returns a string which is then used as-is. Thanks to
Halogen3576 for suggesting an option for a signature in issue 13:
https://github.com/protesilaos/denote-journal/issues/13.
In the case of a function value, users may wish to integrate the
denote-journal package with the denote-sequence package
(denote-sequence version 0.2.0). For example, each new journal
entry should be defined as a new parent sequence. Thus:
(setq denote-journal-signature
(lambda ()
(denote-sequence-get-new 'parent)))
The user option denote-journal-keyword is extended to support a
function value which should return a string or list of strings.
The integration with M-x calendar (when the minor mode
denote-journal-calendar-mode is enabled) is more robust when
highlighting dates that have a Denote journal entry. Thanks to Alan
Schmitt for the patch that improves the check for visible dates
only. This was done in pull request 12: https://github.com/protesilaos/denote-journal/pull/12.
The denote-journal-calendar face is extended to also work when
Emacs is ran in a TTY. Thanks to Ettore Berardi for the original
contribution and for discussing this with me. It was done in pull
request 14: https://github.com/protesilaos/denote-journal/pull/14.
The contribution is less than 15 lines of code, meaning that Ettore
does not need to assign copyright to the Free Software Foundation.
The function denote-journal-path-to-new-or-existing-entry is
tweaked to not kill the buffer of the new file it might generate.
Thanks to jbwfu for the change in pull request 17:
https://github.com/protesilaos/denote-journal/pull/17. The change
is small, meaning that the author does not need to assign copyright
to the Free Software Foundation.
Dates with a single digit in the title of a new journal entry no
longer have a space where the second digit normally is. So something
like October 1 instead of October 1. Thanks to Josh Kingsley for
the patch. The change is small, meaning that Josh does not need to
assign copyright to the Free Software Foundation. It was done in
pull request 24: https://github.com/protesilaos/denote-journal/pull/24.
Thanks to gk2803 for renaming a couple of old symbols to their current names in the commentary of the package. This was done in pull request 10: https://github.com/protesilaos/denote-journal/pull/10.
~/Git/Projects/denote-journal $ git shortlog 0.1.0..0.2.0 --summary --numbered
35 Protesilaos Stavrou
3 Alan Schmitt
2 Ettore Berardi
1 Abdelhak Bougouffa
1 Josh Kingsley
1 gk2803
1 jbwfu
denote-org version 0.2.0denote-sequence
package (denote-sequence version 0.2.0). The block is called
denote-sequence and can be inserted at point with the command
denote-org-dblock-insert-sequence. What this block does is list
the descendants of a given sequence up to a maximum depth. The
presentation is a typographic list of lists to visualise the
hierarchy of the complete sequence, with each set of children nested
under its parent. Thanks to Peter Prevos for the original
implementation in pull request 9 and for subsequent tweaks in pull
request 13. Other changes by me are done to ensure tighter
integration with the denote-sequence package.
All Org dynamic blocks that have a :sort-by-component parameter
can now sort by random and last-modified. This is made possible
by the aforementioned improvements to the core Denote sort mechanism
(The Denote sort mechanism can sort by random and last-modified).
The denote-missing-links Org dynamic block, which can be inserted
at point with the command denote-org-dblock-insert-missing-links
takes an optional :not-regexp parameter. This is a regular
expression of files to omit from the results. Same for the
denote-backlinks block, which can be inserted at point with the
command denote-org-dblock-insert-backlinks.
The denote-files-as-headings Org dynamic block, which can be
inserted at point with the command denote-org-dblock-insert-files-as-headings
now also accepts an optional :exclude-tags parameter. It is either
nil or non-nil and determines whether the heading will have the
file’s #+filetags as its own tags. Thanks to Matt Nolan for
suggesting this idea in issue 14: https://github.com/protesilaos/denote-org/issues/14.
~/Git/Projects/denote-org $ git shortlog 0.1.0..0.2.0 --summary --numbered
42 Protesilaos Stavrou
8 Peter Prevos
denote-sequence version 0.2.0The new commands denote-sequence-find-next-sibling and
denote-sequence-find-previous-sibling move to the next or previous
sibling in the given sequence. When called interactively, they work
relative to the current file. When called from Lisp, they expect a
SEQUENCE argument and its corresponding RELATIVES.
The denote-sequence-dired command is updated to (i) work with a
list value for denote-directory (Set denote-directory to a list of directories),
as noted further above and (ii) benefit from the refinements done to
denote-dired with regard to its revert-buffer-function.
The new command denote-sequence-rename-as-parent makes it easier
to apply the Denote file-naming scheme to an existing file and make
it a new parent sequence. Thanks to Alex Carney for requesting
something along those lines in issue 4:
https://github.com/protesilaos/denote-sequence/issues/4.
[ Remember that Denote is highly adaptable/hackable, meaning that
many of these convenience commands build on top of the core with small
extensions to it. The body of denote-sequence-rename-as-parent is
only 4 lines long, two of which are for an error check. This is
typical of much of what we provide and is how users can always
extend Denote to do something slightly different than what we
support out-of-the-box. ]
Made several other refinements under the hood, including the addition of more tests. In this regard, thanks to Rory Molinari, Peter Prevos, and Ashton Wiersdorf for fixing three bugs in pull requests 5, 8, and 11, respectively:
~/Git/Projects/denote-sequence $ git shortlog 0.1.0..0.2.0 --summary --numbered
61 Protesilaos Stavrou
1 Ashton Wiersdorf
1 Peter Prevos
1 Rory Molinari
denote-markdownNothing of substance.
denote-siloNothing of substance.
]]>tmrBelow are the release notes.
This release introduces several quality-of-life refinements to an already stable and featureful package.
The new minor mode tmr-mode-line-mode controls whether running
timers are displayed on the mode line. More specifically, they are
displayed in the global-mode-string, which can also be set in the
tab-bar-mode (this way the information appears in one place instead
of all the mode lines, assuming default settings).
The exact format of a timer on display is controlled by the user
option tmr-mode-line-format. The number of timers is set with the
option tmr-mode-line-max-timers. The separator between multiple
timers is tmr-mode-line-separator. The length of each timer’s
optional description is subject to tmr-mode-line-max-desc-length.
While the entire indicator can have a prefix, per tmr-mode-line-prefix.
Thanks to Steven Allen for contributing the original version of this
feature in pull request 2: https://github.com/protesilaos/tmr/pull/2.
Steven has assigned copyright to the Free Software Foundation. Further
changes by me, such as to make the timers on the mode line clickable
(which produces a tabulated view, per tmr-tabulated-view).
Timers can optionally trigger a system notification, via the abnormal
hook tmr-timer-finished-functions. The relevant function is
tmr-notification-notify. It used to only support Linux. Now it is
extended to handle Android, Windows, and Haiku.
Thanks to Lucas Quintana for the contribution in pull request 10: https://github.com/protesilaos/tmr/pull/10. Lucas has assigned copyright to the Free Software Foundation.
The tmr-tabulated-view command (alias tmr-list-timers) now
includes a “duration” column, in addition to all the other informative
data on display.
[ Remember that the tabulated view can be used to create, duplicate, edit, etc. the timers. ]
Thanks to jpg for suggesting this in issue 11: https://github.com/protesilaos/tmr/issues/11.
System notifications specify the :app-icon of Emacs. It will be
displayed, if the underlying software supports it (I see it on
Linux, anyway).
The option to play the tmr-sound-file via the abnormal hook
tmr-timer-finished-functions is redone to not rely on the system
shell (implicitly bash). This way, users who use exotic shell
alternatives will not run into any trouble. This is done in response
to a relevant problem that g-gundam was facing with the nushell:
https://github.com/protesilaos/tmr/pull/7.
The function tmr-notification-notify is better at informing the
user that it has produced the warning about the lack of DBUS
support. Before, the notification did not identity itself as
belonging to the tmr package.
The function tmr-running-timers-p is now available as a standalone
function to do what was done before inside other functions. This is
useful for anyone writing custom code on top of tmr. Thanks to
Eugene Mikhaylov for suggesting the idea in issue 9:
https://github.com/protesilaos/tmr/issues/9.
The manual is updated to include whatever necessary from the aforementioned.
modus-themes
to optionally work as the basis for other theme packages. The official
documentation is in the manual of the Modus themes (my manuals are the
only source of truth; blog posts—including this one—become outdated):
https://protesilaos.com/emacs/modus-themes#h:86eb375b-9be4-43ce-879a-0686a524a63b.
I already mentioned how version 2.0.0 of the ef-themes will be
done this way. I am happy to announce that as of this morning the same
is true for my standard-themes. Specifically, version 3.0.0 of the
Standard themes will be built on top of the modus-themes.
Users are now expected to customise the themes via the user options provided by Modus and to load the themes with the relevant Modus commands. Here is a sample configuration:
(use-package standard-themes
:ensure t
:init
;; This makes the Modus commands listed below consider only the
;; Standard themes. For an alternative that includes Modus and all
;; derivative themes (like Standard), enable the
;; `modus-themes-include-derivatives-mode' instead.
(standard-themes-take-over-modus-themes-mode 1)
:bind
(("<f5>" . modus-themes-rotate)
("C-<f5>" . modus-themes-select)
("M-<f5>" . modus-themes-load-random))
:config
;; All customisations here.
(setq modus-themes-mixed-fonts t)
(setq modus-themes-italic-constructs t)
;; Finally, load your theme of choice (or a random one with
;; `modus-themes-load-random', `modus-themes-load-random-dark',
;; `modus-themes-load-random-light').
(modus-themes-load-theme 'standard-light-tinted))
modus-themes-theme macro is flexibleThis morning, I extended the modus-themes-theme macro to optionally
accept custom faces and user options that complement or override those
provided by core Modus (the modus-themes-theme macro is documented
at length with concrete examples in the aforementioned link to the
manual). For the standard-themes, this means that we can inherit all
the goodies from Modus but still deviate stylistically in important
ways wherever necessary. One case where this is evident is the design
of the active mode line: Modus has a flat appearance, while Standard
uses a 3D effect.
Standard does not need to deviate substantively from Modus, though there is no technical constraint in this regard. A derivative theme can implement all the requisite deviations to achieve the exact design it aims for, all while reusing Modus.
My plan is to release all new theme versions in the coming days, depending on my availability. Please let me know if you have any questions or want some things to change.
]]>modus-themes version 5.0.0 and ef-themes version 2.0.0. ]
In previous entries, I outlined how my ef-themes will be built on
top of my modus-themes and then how the modus-themes are partly
redesigned to enable such an arrangement:
I have now finalised the details and merged the changes in the
respective main branch of each Git repository. If you are building
the packages from their latest commit, you might need to delete and
then re-install the package. Otherwise, I expect things to work as
expected. The modus-themes, in particular, should have no obvious
change to its users.
The ef-themes have undergone substantial changes. All of their user
options are deprecated and will no longer have any effect. User
options from the Modus themes now take their place. Furthermore, all
the Ef commands to load a theme are discontinued. The plan is to
re-use the infrastructure of the Modus themes throughout. Concretely,
users must change their configuration to at least this minimal setup:
(use-package ef-themes
:init
;; This is essential to let the Ef themes take over the Modus themes commands.
(ef-themes-take-over-modus-themes-mode 1))
Or this sort, if they want some key bindings and customisations:
(use-package ef-themes
:init
(ef-themes-take-over-modus-themes-mode 1)
:bind
(("<f5>" . modus-themes-rotate)
("C-<f5>" . modus-themes-select)
("M-<f5>" . modus-themes-load-random))
:config
;; All customisations here.
(setq modus-themes-mixed-fonts t))
The commands modus-themes-rotate, modus-themes-select,
modus-themes-toggle, and modus-themes-load-random will also load a
theme interactively.
Loading a specific theme from Lisp works as expected:
(load-theme 'ef-cyprus :no-confirm)
To do the same while also triggering the
modus-themes-after-load-theme-hook as well as the
modus-themes-disable-other-themes:
(modus-themes-load-theme 'ef-summer)
To load a theme at random, do this:
(modus-themes-load-random)
(modus-themes-load-random 'dark) ; dark only (alternative `modus-themes-load-dark')
(modus-themes-load-random 'light) ; light only (alternative `modus-themes-load-light')
To not take over the Modus themes completely and to instead allow the Modus commands to act on both Modus and Ef themes, enable this minor mode instead:
(modus-themes-include-derivatives-mode 1)
And make sure you disable ef-themes-take-over-modus-themes-mode
in this case.
All the user options of the Modus themes are now available. For
example, there was no option in the past to disable bold and italic
styles from the Ef themes. They are now disabled by default, unless
the user sets modus-themes-bold-constructs and modus-themes-italic-constructs
to non-nil values.
Additionally, the Ef themes now enjoy wider face/package support.
Advanced users who were relying on the ef-themes-with-colors or any
other Ef functionality will have to do the same with the Modus
framework. Everything is possible, plus I am happy to help with the
transition if you have any questions.
The Modus themes benefit from this redesign because their code is made to be more generic and to not hardcode certain assumptions. It also makes sense for them to be useful as a dependency for other themes, as they are part of core Emacs, which is convenient.
Remember that everything is still in development. I will expand the manuals and make sure everything is in order for the next major version of each of these two projects.
If everything goes well, expect my standard-themes to be redone on
top of the modus-themes. But not for the doric-themes: those are
quite different in style and overall design.
modus-themes more flexible so they
can be used as a the basis for other theme packages and (ii) make the
ef-themes the first project to benefit from this development. Having
the Modus themes as a foundation gives us all of their customisability
and extensive face coverage for little extra work. The themes are well
tested and are also shipped with core Emacs. It all fits together.
In this article, I give you the big picture view of how this is supposed to work. Remember that the only source of truth for my packages is their corresponding manual. Any blog post is useful the time it is written but will eventually go out of date.
When we define a theme, we essentially add properties to a symbol. In its simplest form, this is how:
(put 'my-test-symbol 'my-test-proerty "Hello world test value")
Evaluate the above and then try the following:
(get 'my-test-symbol 'my-test-proerty)
;; => "Hello world test value"
The function custom-declare-theme does the heavy lifting, while the
deftheme macro streamlines most of that work. Still, the point is
that we have symbols whose properties we can access and, thus, we can
filter by any given property. To make things even better, we can add
arbitrary properties to a theme. Here is a real scenario of
_in-development code that might change:
(get 'modus-operandi 'theme-properties)
;; => (:kind color-scheme :background-mode light :family modus-themes :modus-core-palette modus-themes-operandi-palette :modus-user-palette modus-operandi-palette-user :modus-overrides-palette modus-operandi-palette-overrides)
The theme-properties has as a plist value. Its Modus-specific
properties are references to variables that we can use to do our work,
such as to put together a theme palette that combines the relevant
overrides with the core entries.
When we declare a theme with custom-declare-theme, we make it known
to Emacs by adding it to the custom-known-themes. When we eventually
load a theme, its symbol gets stored in the custom-enabled-themes.
Knowing that themes have properties, we can filter those lists
accordingly. With my current development code, I can do this, for
example:
(defun my-demo-is-modus-p (theme)
"Return non-nil if THEME has `modus-themes' :family property."
(when-let* ((properties (get theme 'theme-properties))
(family (plist-get properties :family)))
(eq family 'modus-themes)))
(seq-filter #'my-demo-is-modus-p custom-known-themes)
;; => (modus-vivendi-tritanopia modus-vivendi-tinted modus-vivendi modus-vivendi-deuteranopia modus-operandi-tritanopia modus-operandi-tinted modus-operandi modus-operandi-deuteranopia)
The next step from here is to make all the Modus infrastructure rely on generic functions and methods for working with themes. Then any package can provides its own method for returning a list of desired themes.
Emacs Lisp has a concept of generic functions, which it borrows from
Common Lisp. The general idea is to have a single symbol, like
modus-themes-get-themes whose implementation details are
instantiated via specialised methods. For example, when a minor mode
is active, a given method takes effect, thus changing what
modus-themes-get-themes actually does.
The default implementation is this:
(cl-defgeneric modus-themes-get-themes ()
"Return a list of all themes with `modus-themes' :family property."
(modus-themes-get-all-known-themes 'modus-themes))
The function modus-themes-get-all-known-themes has a filter like the
one I demonstrated in the code block further above. By default, this
is what I get when I run the aforementioned generic function:
(modus-themes-get-themes)
;; => (modus-operandi modus-operandi-tinted modus-operandi-deuteranopia modus-operandi-tritanopia modus-vivendi modus-vivendi-tinted modus-vivendi-deuteranopia modus-vivendi-tritanopia)
The beauty of this design is that another package can define a method
to make the same code return something else. This is how I do it in
the current development target of the ef-themes (again, the actual
code might change):
(cl-defmethod modus-themes-get-themes (&context (ef-themes-take-over-modus-themes-mode (eql t)))
(modus-themes-get-all-known-themes 'ef-themes))
Notice that this method has a &context, which is the scenario in
which it is relevant. In this case, we have a minor mode that
activates the method when it is enabled:
(define-minor-mode ef-themes-take-over-modus-themes-mode
"When enabled, all Modus themes commands consider only Ef themes."
:global t
:init-value nil)
This minor mode does not have anything in its body. It does not need
to, because the define-minor-mode macro already instantiates the
parts we care about. Namely, when we call the function defined by the
minor mode (i.e. ef-themes-take-over-modus-themes-mode), it toggles
the value of the variable ef-themes-take-over-modus-themes-mode
(functions and variables have separate namespaces in Emacs Lisp and
thus the same symbol can be in both places). Our method then becomes
relevant when the user enables the minor mode:
(ef-themes-take-over-modus-themes-mode 1)
And now the generic function modus-themes-get-themes does something
else:
(modus-themes-get-themes)
;; => (ef-winter ef-tritanopia-light ef-tritanopia-dark ef-trio-light ef-trio-dark ef-symbiosis ef-summer ef-spring ef-rosa ef-reverie ef-owl ef-night ef-melissa-light ef-melissa-dark ef-maris-light ef-maris-dark ef-light ef-kassio ef-frost ef-elea-light ef-elea-dark ef-eagle ef-duo-light ef-duo-dark ef-dream ef-deuteranopia-light ef-deuteranopia-dark ef-day ef-dark ef-cyprus ef-cherie ef-bio ef-autumn ef-arbutus)
Since all the Modus functions are redesigned to work with this generic
function, we can now use commands like modus-themes-select or even
modus-themes-list-colors for any of those themes.
As a bonus, we can now seamlessly blend Modus themes with their
derivatives. Imagine a user who wants to invoke the command
modus-themes-load-random (or its variants for light and dark themes)
and have it consider the likes of modus-operandi and ef-dream.
Users can opt in to this feature via the minor mode that the Modus
themes provide called modus-themes-include-derivatives-mode. It is
the same ideas as the minor mode for the Ef themes, mentioned above:
(define-minor-mode modus-themes-include-derivatives-mode
"When enabled, all Modus themes commands cover derivatives as well.
Otherwise, they only consider the `modus-themes-items'.
Derivative theme projects can implement the equivalent of this minor
mode plus a method for `modus-themes-get-themes' to filter themes
accordingly."
:global t
:init-value nil)
(cl-defmethod modus-themes-get-themes (&context (modus-themes-include-derivatives-mode (eql t)))
(modus-themes-get-all-known-themes nil))
This is what happens when I load both the modus-themes and the
ef-themes and enable this “all good ones fit” minor mode:
(modus-themes-include-derivatives-mode 1)
(modus-themes-get-themes)
;; => (modus-operandi modus-operandi-tinted modus-operandi-deuteranopia modus-operandi-tritanopia modus-vivendi modus-vivendi-tinted modus-vivendi-deuteranopia modus-vivendi-tritanopia ef-winter ef-tritanopia-light ef-tritanopia-dark ef-trio-light ef-trio-dark ef-symbiosis ef-summer ef-spring ef-rosa ef-reverie ef-owl ef-night ef-melissa-light ef-melissa-dark ef-maris-light ef-maris-dark ef-light ef-kassio ef-frost ef-elea-light ef-elea-dark ef-eagle ef-duo-light ef-duo-dark ef-dream ef-deuteranopia-light ef-deuteranopia-dark ef-day ef-dark ef-cyprus ef-cherie ef-bio ef-autumn ef-arbutus)
And when I no longer want to include everything, I just disable the minor mode:
(modus-themes-include-derivatives-mode -1)
(modus-themes-get-themes)
;; => (modus-operandi modus-operandi-tinted modus-operandi-deuteranopia modus-operandi-tritanopia modus-vivendi modus-vivendi-tinted modus-vivendi-deuteranopia modus-vivendi-tritanopia)
It is a thing of beauty!
I am still experimenting with some of the technicalities involved. In
principle, derivative themes will (i) depend on the modus-themes,
(ii) define each of their themes using the modus-themes-theme macro,
and (iii) specify how/when they affect the behaviour of the generic
function modus-themes-get-themes.
The code I am working on will soon be available in the respective
main branch of modus-themes.git and ef-themes.git. I think this
gives us the tools to realise the full potential of the Modus themes.
Finally, it is not just package authors that can benefit from this development. Users may also curate their themes with something as basic as this:
(cl-defmethod modus-themes-get-themes ()
'(modus-operandi ef-eagle modus-vivendi-tinted ef-melissa-dark))
(modus-themes-get-themes)
;; => (modus-operandi ef-eagle modus-vivendi-tinted ef-melissa-dark)
In this method, there is no function involved for returning a list of themes nor an opt-in clause. It simply hardcodes a list of themes. The point is that it works! The approach with the minor mode will usually be better and is easy enough. It is all a matter of empowering personal preference, which is the Emacs-y outlook, after all. I expect users to define their own collections, as they see fit.
Have fun!
Highly accessible themes, conforming with the highest standard for colour contrast between background and foreground values (WCAG AAA). They also are optimised for users with red-green or blue-yellow colour deficiency.
The themes are very customisable and provide support for a wide range of packages. Their manual is detailed so that new users can get started, while it also provides custom code for all sorts of more advanced customisations.
Since August 2020, the original Modus themes (modus-operandi,
modus-vivendi) are built into Emacs version 28 or higher. Emacs 28
ships with modus-themes version 1.6.0. Emacs 29 includes version
3.0.0. Emacs 30 provides version 4.4.0. Version 4 is a major
refactoring of how the themes are implemented and customised. Such
major versions are not backward-compatible due to the limited
resources at my disposal to support multiple versions of Emacs and of
the themes across the years.
modus-themesThe ef-themes are a collection of light and dark themes for GNU
Emacs that provide colourful (“pretty”) yet legible options for users
who want something with a bit more flair than the modus-themes (also
designed by me).
ef-themes4.0.0 of my modus-themes, users can override the
palette of one or all the themes. They can change the applicable
colours and how those are mapped to semantic elements. For example,
“heading level 1” will apply to Org, Markdown, Info, and anything else
that defines a relevant face. In principle, Modus can derive any
theme. The advantage is that we get battle tested support for a wide
range of packages, theme-wide consistency and attention to detail, and
all the familiar flexibility of customisation, while only defining new
colour values and/or their mappings. Plus, the Modus themes are built
into Emacs.
When I first designed the ef-themes, Modus did not have the
aforementioned capabilities. As such, Ef had to be implemented from
scratch. Over time, the two projects have converged in terms of
overall approach. There still are some notable differences, namely, Ef
is less customisable and extensive than Modus. This is about to change.
I am working on a thoroughgoing redesign of the Ef themes to make them
essentially load either modus-operandi (main light theme) or
modus-vivendi (main dark theme) with the relevant palette overrides.
All the Ef themes will look the same as they do now, but under the
hood things will become much simpler. What is currently a standalone
“theme” object will be reduced to a palette definition, essentially an
alist.
In a future publication, I will document all the breaking changes. In
short, all customisation shall be done via the Modus user options. Ef
will only implement its palettes. As for my doric-themes and
standard-themes those will remain unchanged for the time being.
Standard might go the way of Ef, but Doric will probably continue to
be its own thing. At any rate, these issues will be examined when
their time is right.
Stay tuned for more. In the meantime, enjoy version 1.11.0 of the Ef
themes that I released earlier this week!
The ef-themes are a collection of light and dark themes for GNU
Emacs that provide colourful (“pretty”) yet legible options for users
who want something with a bit more flair than the modus-themes (also
designed by me).
ef-themesHighly accessible themes, conforming with the highest standard for colour contrast between background and foreground values (WCAG AAA). They also are optimised for users with red-green or blue-yellow colour deficiency.
The themes are very customisable and provide support for a wide range of packages. Their manual is detailed so that new users can get started, while it also provides custom code for all sorts of more advanced customisations.
Since August 2020, the original Modus themes (modus-operandi,
modus-vivendi) are built into Emacs version 28 or higher. Emacs 28
ships with modus-themes version 1.6.0. Emacs 29 includes version
3.0.0. Emacs 30 provides version 4.4.0. Version 4 is a major
refactoring of how the themes are implemented and customised. Such
major versions are not backward-compatible due to the limited
resources at my disposal to support multiple versions of Emacs and of
the themes across the years.
modus-themesef-themes are a collection of light and dark themes for GNU
Emacs that provide colourful (“pretty”) yet legible options for users
who want something with a bit more flair than the modus-themes (also
designed by me).
ef-themesBelow are the release notes.
This version introduces minor refinements to a stable package.
Emacs 31 is the current development target of Emacs. The new faces are
header-line-inactive, package-mark-delete-face, package-mark-install-face,
minibuffer-nonselected.
The active and inactive mode lines now have a subtle box/border around them on graphical Emacs. In non-graphical sessions, an underline is applied instead. This makes mode lines easier to stand out even when the buffer is showing a background that is of a similar colour to their own background.
These are the buttons that appear in the Custom buffers that we reach
through various means such as M-x customize. Like with the mode
lines, they use a box/border around where possible, else fall back to
an underline.
M-x calendar always looks the sameBefore, it would look different if the Calendar was produced via M-x
calendar as opposed to the Org date selection interface. This is
because they apply different faces to the current date. Those are now
reworked to look the same.
In the Notmuch email client, the messages in a thread can be collapsed/minimised to just a heading. Those used to have only a subtle background, which would be enough to differentiate them from the body of the email but not from each other when all were collapsed. Now the themes add an overline to each heading, if supported by the underlying display engine (i.e. graphical Emacs), which should make it easier to spot them.
This is because the header line has a distinct background already, so we want to avoid exaggerations.
ef-themes-rotate and ef-themes-load-random can be silentThey now accept an optional SILENT parameter that inhibits the
message they otherwise print. Thanks to Sean Devlin for the
contribution in pull request 59: https://github.com/protesilaos/ef-themes/pull/59.
Note that any function can be silenced with something like this:
(defun my-wrapper-of-some-function ()
(let ((inhibit-message t))
(some-function)))
4.1.0 of
Denote will provide the option to define custom identifier schemes.
Here I explain one of the examples I have included in the manual,
specifically, the idea of recording the day of the week as part of the
date+time stamp. Monday is A, Tuesday is B, and so on.
The default Denote identifier is a compact representation of the date
and time, with the letter T in between. For example:
20250924T190920
The letter T does not have any special meaning. It simply delimits
the two constituents of the identifier. Users who want to record the
day of the week can ultimately derive identifiers that look like these:
20250922A082941
20250923B140652
20250924C191038
And with some spaces for didactic purposes:
2025 09 22 A 08 29 41
2025 09 23 B 14 06 52
2025 09 24 C 19 10 38
[ Actually, users can produce identifiers with such spaces, but I will leave this as an exercise for interested parties. ]
To get the day of the week as a string that holds a number, we use the
%u format specifier of the function format-time-string. Today is
Wednesday, thus:
(format-time-string "%u")
;; => "3"
Based on this, we know that we need a function that takes such a string and returns the corresponding capital letter. Here is one way of achieving as much:
(defun my-denote-get-day-of-week-as-alpha (day)
"Return the corresponding capital letter for DAY.
DAY is a string holding a number, as the return value of the %u
specifier in `format-time-string'.
If DAY is a number, convert it to a string and then return its
corresponding letter."
(pcase (if (numberp day) (number-to-string day) day)
("1" "A")
("2" "B")
("3" "C")
("4" "D")
("5" "E")
("6" "F")
("7" "G")
(_ (error "The day `%S' is not among 1-7" day))))
The pcase macro does the heavy lifting here. We basically test for
string equality and return a new string or throw an error. Testing the
code, I get these results:
(my-denote-get-day-of-week-as-alpha "3")
;; => "C"
(my-denote-get-day-of-week-as-alpha 3)
;; => "C"
(my-denote-get-day-of-week-as-alpha "10")
;; => ERROR
(my-denote-get-day-of-week-as-alpha 10)
;; => ERROR
%u specifierDenote stores the format-time-string specifiers for its default
identifier in the variable denote-date-identifier-format. The value
is "%Y%m%dT%H%M%S", which is what derives the aforementioned
standard identifier.
We already noted that we do not want that literal T over there. So
let us make our format the same as the original one except that we
include %u in place of T. A function call that would yield the
desired result is this:
(replace-regexp-in-string "T" " %u " denote-date-identifier-format t)
;; => "%Y%m%d %u %H%M%S"
Notice that I add a space before and after the %u. These are
delimiters that we will use to split the string more easily into its
DATE, DAY, TIME components. But one step at a time. We first
need to define this as a variable:
[ Technically, this is not a normal variable because of defconst
instead of the more common defvar, but we do not need to consider
such details. ]
(defconst my-denote-date-identifier-format
(replace-regexp-in-string "T" " %u " denote-date-identifier-format t))
Our next task is to write a small function that takes a date argument
and returns a string whose pattern follows what we did in the previous
section. For example, if the current date is 2025-09-24, the day is
Wednesday, and the time is 19:30:00, we expect an identifier like
20250924C193000.
To keep things simple, we will not go into how Emacs represents dates
internally. It does not matter, anyway, because Denote handles that
part internally. All we need to test our function is to use the return
value of the function current-time. Actually, even that is optional
because format-time-string defaults to that if it is not given an
explicit date as an argument.
As such, here is how we get the string "DATE DAY TIME", based on
what we have:
(format-time-string my-denote-date-identifier-format)
;; => "20250924 3 193347"
Remember those spaces from earlier? We need them now to split this string into a list of three strings, so that we can isolate the day in the middle. Once we have just the day, we can pass it to the function we wrote earlier that gives us the corresponding capital letter.
To split a string at the spaces, we do this (read the split-string
doc string for further details):
(split-string STRING)
Which means:
(split-string (format-time-string my-denote-date-identifier-format))
;; => ("20250924" "3" "193626")
We get back a list of strings, so the day is retrieved thus:
(nth 1 '("20250924" "3" "193626"))
;; => "3"
Use (nth 0 ...) for the date and (nth 2 ...) for the time.
Now that we know all this, we can piece it together into a single function. Here is what we will do, which I shall explain in further detail below:
(defun my-denote-format-date (date)
"Format DATE using `my-denote-date-identifier-format'.
Represent the day of the week with `my-denote-get-day-of-week-as-alpha'."
(pcase-let* ((identifier-string (format-time-string my-denote-date-identifier-format date))
(`(,date ,day ,time) (split-string identifier-string))
(day-as-letter (my-denote-get-day-of-week-as-alpha day)))
(concat date day-as-letter time)))
The pcase-let* macro uses “destructuring” to pattern match the return
value and bind it to the variables we name (also see my free (gratis and
libre) book: Emacs Lisp Elements).
This is the short way of doing the following:
(defun my-denote-format-date (date)
"Format DATE using `my-denote-date-identifier-format'."
(let* ((identifier-string (format-time-string my-denote-date-identifier-format date))
(strings (split-string identifier-string))
(date (nth 0 strings))
(day (nth 1 strings))
(time (nth 2 strings))
(day-as-letter (my-denote-get-day-of-week-as-alpha day)))
(concat date day-as-letter time)))
Use whichever style you prefer.
The point is that we (i) take the return value of format-time-string
with the spaces, as I showed above, (ii) split it at the spaces into
three strings, (iii) assign each of those strings to a variable local
to this function, (iv) convert the day from a number to a capital
letter, and (v) put it all back together without those spaces. The
result is this:
(my-denote-format-date (current-time))
;; => "20250924C194219"
denote-get-identifier-functionThe variable denote-get-identifier-function has the symbol of a
function as its value. Its documentation describes the technicalities.
Though, again, we do not need to understand the finer points to
achieve our goal. Since our custom identifier is almost the same as
the default, we can copy the original function that Denote relies on
and make a small change to it so that it uses the my-denote-format-date
we just wrote. Behold:
(defun my-denote-generate-identifier-as-date (initial-identifier date)
"Generate an identifier based on DATE.
If INITIAL-IDENTIFIER is not already used, return it. Else, if it is
possible to derive an identifier from it, return this identifier.
Else, use the DATE. If it is nil, use `current-time'.
Slightly modified version of `denote-generate-identifier-as-date'."
(let ((denote-used-identifiers (or denote-used-identifiers (denote--get-all-used-ids))))
(cond ((and initial-identifier
(not (gethash initial-identifier denote-used-identifiers)))
initial-identifier)
((and initial-identifier
(string-match-p denote-date-identifier-regexp initial-identifier)
(date-to-time initial-identifier))
(denote--find-first-unused-id-as-date initial-identifier))
(t
(denote--find-first-unused-id-as-date
(my-denote-format-date (or date (current-time))))))))
The internal workings do not matter right now, though I am happy to
explain them elsewhere. Our priority is to find where my-denote-format-date
is used. The only difference between what we have here and the
original is that our final line uses my-denote-format-date, while
the original does a format-time-string with the denote-date-identifier-format
(which we saw further above when we replaced T with %u).
Finally, bind denote-get-identifier-function to the symbol of the
custom function to start using it:
(setq denote-get-identifier-function #'my-denote-generate-identifier-as-date)
All Denote files will henceforth look like this:
@@20250924C190301--this-is-a-test__denote_testing.txt
Remember that the @@ exists for custom identifiers and that all the
components of the file name can be reordered.
This covers it! The manual has more examples and I always am eager to help anyone who needs to do something a little bit differently.
Denote is a simple note-taking tool for Emacs. It is based on the idea that notes should follow a predictable and descriptive file-naming scheme. The file name must offer a clear indication of what the note is about, without reference to any other metadata. Denote basically streamlines the creation of such files while providing facilities to link between them.
Denote’s file-naming scheme is not limited to “notes”. It can be used for all types of file, including those that are not editable in Emacs, such as videos. Naming files in a consistent way makes their filtering and retrieval considerably easier. Denote provides relevant facilities to rename files, regardless of file type.
[ I have a page with all my Emacs packages, which includes all the Denote extensions I maintain: https://protesilaos.com/emacs. ]
denotedenote and the various
other extensions for it that I maintain. I will share more details in
the release notes, hopefully before the end of this month. Just to
give you a forewarning about a potentially massive boost for advanced
users or developers: the ability to specify a completely custom
identifier scheme for Denote file names.
By default, Denote constructs a file name to have, at minimum, an
identifier as a compact representation of the date and time. It looks
like this: 20250920T120924. The default file will also have a title
and keywords, based on user input at the relevant minibuffer prompts,
so it will look like this:
20250920T120924--denote-custom-identifiers__emacs_programming.txt
Instead of hardcoding the scheme of an identifier as a compact
representation of a date and time, we provide the variable
denote-get-identifier-function. Its value is the symbol of a
function, which is called to yield an identifier, as a string.
I have written a few examples of this in the Denote manual, grouped
under this section: https://protesilaos.com/emacs/denote#h:3048f558-7d84-45d6-9ef2-53055483e801.
Read the documentation of denote-get-identifier-function for the
remaining technicalities.
The identifier can be anything from an automatic incremental numbering scheme to something that always asks for user input to produce a more information-dense result. To illustrate the potential of the latter concept, the manual elaborates on the use-case of recording data about academic publications, such as the editor, authors, and publisher, through a series of minibuffer prompts. The end result is a unique identifier with the help of some extra helper functions.
Custom identifiers are prefixed with the @@ file name component
separator. This way they are unambiguous and can be retrieved with
certainty. More so since the Denote file name components can be
presented in any order, per the user option denote-file-name-components-order.
Custom identifiers are the culmination of many changes over the years
to make the Denote file-naming scheme as flexible as possible. It is
super effective out-of-the-box and can then be particularised to
specific workflows with ease. These are not mutually exclusive, as
users can still rely on the regular Denote functionality for most
cases and only deviate from it with bespoke functions where necessary.
For instance, to have a special variant of denote-rename-file that
applies a custom identifier scheme instead of the default, users can
write a wrapper command like this:
(defun my-denote-rename-file-with-my-custom-identifier ()
"Like `denote-rename-file' but use `my-denote-get-custom-identifier' instead."
(interactive)
(let ((denote-get-identifier-function #'my-denote-get-custom-identifier))
(call-interactively 'denote-rename-file)))
Imagine doing this for files in a specific folder where the standard identifier provided by Denote does not add much value (beside being a unique match for the linking mechanism, that is).
The Denote manual is full of examples like the above. These sort of small and elegant extensions are possible owning to the design of the code into easily adaptable functions.
We keep going from strength to strength and I am delighted with the results. I hope you make good use of the tools on offer. Let me know if there is anything more you need: I am confident we can make it happen.
Much of the thought and work that went is to this project is thanks to user mentalisttraceur and long-time contributor Jean-Philippe Gagné Guay.
Denote is a simple note-taking tool for Emacs. It is based on the idea that notes should follow a predictable and descriptive file-naming scheme. The file name must offer a clear indication of what the note is about, without reference to any other metadata. Denote basically streamlines the creation of such files while providing facilities to link between them.
Denote’s file-naming scheme is not limited to “notes”. It can be used for all types of file, including those that are not editable in Emacs, such as videos. Naming files in a consistent way makes their filtering and retrieval considerably easier. Denote provides relevant facilities to rename files, regardless of file type.
[ I have a page with all my Emacs packages, which includes all the Denote extensions I maintain: https://protesilaos.com/emacs. ]
denoteSources:
show-fontBelow are the release notes.
This major update introduces support for fonts that cover more than the Latin script. It also expands the available functionality with quality-of-life refinements.
The commands show-font-tabulated (alias show-font-list) and
show-font-select-preview can now generate a preview for fonts that
are optimised to display the aforementioned languages. Each language
provides its own user option to control the sample text it displays.
The naming pattern show-font-LANGUAGE-sample.
Of those, I only know Greek and thus wrote the value of
show-font-greek-sample, namely: "Πρωτεσίλαος ο φιλόσοφος του οποίου
τα έργα βρίθουν αστειισμών". For the others I used translation
software to get the equivalent of "Protesilaos does not read
LANGUAGE". Please let me know if there are any mistakes in this
regard. I was thinking of writing something a bit more funny, but was
concerned the joke may not translate well.
While I have written functions that test if a given font can display a range of characters, this approach is computationally intensive if we need to check for many code points across multiple fonts.
The alternative is to maintain lists of known font families that are meant to work with the given language. Those generally support Latin as well, but the idea is to let them shine in the language they are meant to be used for.
For example, here is how we know that a font family is meant to display Arabic script:
(defconst show-font-arabic-families
'("AlArabiya" "AlBattar" "AlHor" "AlManzomah" "AlYarmook"
"Dimnah" "Hani" "Haramain" "Hor" "Kayrawan" "Khalid" "Mashq"
"Nagham" "Noto Kufi Arabic" "Noto Naskh Arabic" "Noto Sans Arabic"
"Rehan" "Sharjah" "Sindbad")
"List of families that specialise in Arabic.
Also see `show-font-greek-families' for the rationale of grouping font
families in distinct variables.")
The list is not exhaustive and I am always eager to expand it. Just let me know.
I learnt about these font families through trial and error by (i)
installing them on my Debian system and (ii) searching online for
common samples of them. Do apt search -n fonts- to check the
relevant packages.
As with the natural languages, there are some fonts that specialise in
displaying symbols. For example, MathJax has a bunch of fonts for
showing those fancy formulas in the processed output of LaTeX
documents. Again, there is a defconst for each of those types of
font listing the known families. The concomitant user options are:
show-font-mathematics-sampleshow-font-music-sampleshow-font-symbols-sampleThe show-font-hidden-families lists the fonts that are not known to
cause problems. They do not render properly any of the supported
samples and I am not sure even when they claim to support a certain
set of characters (e.g. show-font--displays-latin-p returns
non-nil). If you think there is a mistake here, please contact me.
While in the buffer produced by show-font-tabulated (alias
show-font-list), type RET to get a complete preview of the font
family of the current line. This is the same as invoking the command
show-font-select-preview and then selecting the given family.
The command called by that key binding is show-font-tabulated-select-preview.
A hint of it is also shown in the tabulated list header.
As above, type w in the tabulated view to copy the name of the font
family to the kill-ring. The command is show-font-tabulated-copy-name
and there is also a hint of it in the tabulated list header.
If you want maximalist themes in terms of colour, check my ef-themes
package. For something in-between, which I would consider the best
“default theme” for a text editor, opt for my modus-themes.
doric-themesBelow are the release notes.
This version brings several refinements to the package, as well as two new themes.
Most of the Doric themes feel monochromatic. By contrast, the
doric-beach (light) and doric-valley (dark) are dichromatic: they
combine warm hues, such as orange, with cold hues like teal. They
still are minimalistic, like the rest of the family, but appear a bit
more playful than their more austere counterparts.
[ The doric-oak and doric-pine are similar in this regard. ]
All diff interfaces, including Magit, diff-mode, and Ediff now rely on a comprehensive new subset of colours. They use colour-coded backgrounds throughout, whereas before they had just colour-coded text. The problem with highlighting only text is that it is harder to (i) discern the colour and thus (ii) quickly estimate the boundaries of a change.
For Magit in particular, there are distinct styles for the highlighted/current diff hunk, all other diff hunks, as well as the common word-wise (“refined”) diffs that all interfaces share.
The changes here are subtle but should contribute to a more pleasant experience, owning to the more careful emphasis of information. “Scheduled” entries are easier to notice, as they are distinct from other events. Date headings also have a more clear distinction from the tasks they contain.
For the M-x calendar, in particular, month and date headings are revised to be consistent with the Org agenda, while the diary and holiday faces are redone to look harmonious in context. Thanks to Amin Bandali for checking those and suggesting some tweaks.
In various contexts, such as in git-mode commit message comments or Dired buffers, there are pieces of text that conceptually are headings. Those are rendered in a distinct style than other generic “bold” faces, to better perform their function.
Those now have a fine background colour, coded to the type of warning.
lin and pulsar packagesThose define faces that affect the background of a line. These packages now feel right when used in tandem with the doric-themes.
]]>Sources:
show-fontBelow are the release notes.
This version expands the capabilities of the font preview mechanism to handle emoji and icon fonts (i.e. those with codepoints in the Unicode Private Use Area).
Both the show-font-select-preview and show-font-tabulated commands
now handle these types of font.
Previews for emoji or icon fonts are done using the string of
characters set to the variable show-font-emoji-sample or
show-font-icon-sample. These symbols are user options.
doric-themes
package for Emacs. These combine gold with teal hues. doric-beach is
the light theme and doric-valley is its dark counterpart. Here are
the screenshots:
The character of the themes is well-defined, though I may still make some small tweaks. Expect these and other improvements to be available in the next stable version of the doric-themes, which I expect to publish some time before the end of August.
The Doric themes use few colours and will appear monochromatic in many contexts. They are my most minimalist themes. Styles involve the careful use of typographic features and subtleties in colour gradients to establish a consistent rhythm.
If you want maximalist themes in terms of colour, check my ef-themes
package. For something in-between, which I would consider the best
“default theme” for a text editor, opt for my modus-themes.
doric-themessxhkdrc files. SXHKD is the Simple
X Hot Key Daemon which is commonly used in minimalist desktop sessions
on Xorg (I use it with bspwm, herbstluftwm, and i3wm). The sxhkdrc
file configures key chords, binding them to commands. For the
technicalities, read the man page sxhkd(1).
sxhkdrc-modeThe package is stable and gets the job done. This version introduces a small new feature to restart the daemon from inside Emacs. Thanks to Jonathan Neidel for making the suggestion in issue 1: https://github.com/protesilaos/sxhkdrc-mode/issues/1.
The command sxhkdrc-mode-restart sends a signal to the sxhkd
process which causes it to restart, thus reloading its configuration
file. Use this after modifying the sxhkdrc to make the new changes
available.
The function sxhkdrc-mode-auto-restart can be assigned to the
sxhkdrc-mode-hook to automatically reload the daemon after the
sxhkdrc file is saved (well, technically, after the file which is
using the sxhkdrc-mode is saved).
use-packageThe project’s README.md includes this sample configuration:
(use-package sxhkdrc-mode
:ensure t
:mode "sxhkdrc.*" ; if you want more than just "sxhkdrc"
:commands (sxhkdrc-mode-restart)
:hook (sxhkdrc-mode . sxhkdrc-mode-auto-restart))
If you want maximalist themes in terms of colour, check my ef-themes
package. For something in-between, which I would consider the best
“default theme” for a text editor, opt for my modus-themes.
doric-themesBelow are the release notes.
oak and pine themesThese explore the woody+earthly style:
doric-oak has dark text against an ochre background, with accents
of green.
doric-pine uses light text against a dark green background, with
accents of brown.
All diff-related interfaces, including Magit, diff-mode, and Ediff, now use colour-coded background values where appropriate. This is done to improve usability, as it otherwise can be hard to discern the boundaries of individual diff hunks.
In message-mode, Gnus, and related interfaces all quoted/cited
message level now conforming with an odd VS even colouring scheme. The
style is still subtle, but now the individual levels of depths are
easier to spot.
This morning, I got the paperback version of the book. It is a thing of beauty!
Thanks to Peter for working on this and for showing how Emacs can also be used as a professional publishing tool.
Buy the book, check the source code, and watch the videos:
Also read the Why Use Emacs essay on Peter’s website: https://lucidmanager.org/productivity/why-use-emacs/.
]]>doric-themes
collection. Both explore the woody, earthly, chthonic motif. Here they
are:
I may still make further refinements, though the character of the
themes is set. Once I am done, I will publish them as part of version
0.3.0 of the package.
The Doric themes use few colours and will appear monochromatic in many contexts. They are my most minimalist themes. Styles involve the careful use of typographic features and subtleties in colour gradients to establish a consistent rhythm.
If you want maximalist themes in terms of colour, check my ef-themes
package. For something in-between, which I would consider the best
“default theme” for a text editor, opt for my modus-themes.
doric-themesI had a ~2-hour-and-30-minute discussion with Christian from the @linkarzu YouTube channel: https://www.youtube.com/@linkarzu.
We talked about Emacs, the underlying idea of an integrated computing environment, how NeoVim effectively took the source of Vim and infused with the spirit of Emacs, thinking in terms of extensibility and cohesion, why purists cannot impose an arbitrary limit to extensibility, and more.
We also discuss other interesting topics, such as Debian and Arch Linux, how free software emphasises freedom, why it is okay to earn an income while contributing to free software, custom keyboards and ergonomics, my journey into computing, alcohol and substance abuse, and other issues of everyday life.
Thanks to Christian for inviting me to this chat! I had a good time and wish all the best with the @linkarzu channel!
]]>Amin has since done the work and documented it here: https://kelar.org/~bandali/2025/07/25/protesilaos-videos-archive.html. The publication also points to the Python script which performs the requisite operations. The script relies on the source code of my website to extract the relevant data. Amin describes the underlying technical considerations, given the large number of videos that needed to be downloaded from YouTube and uploaded to the Internet Archive.
On my part, I made a small change to the template of my website that produces the HTML output of entries with an embedded video. Those have links to the source on YouTube and, now, to the corresponding Internet Archive page. For example, my latest “Prot Asks” video with Ihor, the Emacs Org mode maintainer, is available here: https://archive.org/details/prot-codelog-2025-07-26-prot-asks-ihor-emacs-org-maintainer-history-travel-material-science/.
These “raw links”, as opposed to the embedded frame, are also helpful for users who receive my publications via RSS/Atom feeds (and I do share the entire blog post, by the way, rather than an excerpt because that is the most convenient way to read RSS).
Thanks to Amin for this initiative!
]]>Christian, the author of @linkarzu, learnt about me via feedback about the roundtable and contacted me shortly thereafter. We agreed to have a video exchange focused on Emacs. The idea is for me to share my thoughts on the matter and demonstrate my day-to-day workflow.
The live event is for paying members of @linkarzu and can be checked here: https://www.youtube.com/live/KD9uiDdQDzI (Thursday, July 31 at 15:00 Europe/Athens). The recording of the video will be available to everyone shortly afterwards and will be free of charge.
Christian wanted to check with me if this practice is acceptable. I confirmed that it is fine by me and am looking forward to a nice chat!
Once the video is published, I will also share it on my website.
]]>cursoryBelow are the release notes.
This version contains small additions to a stable package.
The new function cursory-set-last-or-fallback makes it easier to set
the last known preset when starting Emacs or via a hook such as after-init-hook.
Internally, it takes care to fall back to a set of default values that
always work.
Before the introduction of cursory-set-last-or-fallback users had to
do something like this:
;; Old way of setting a preset:
(cursory-set-preset (or cursory-last-selected-preset cursory-recovered-preset 'box))
;; Old way of doing the above via a hook:
(add-hook 'after-init-hook (lambda () (cursory-set-preset (or cursory-last-selected-preset cursory-recovered-preset 'box))))
Those would also fail if the named preset did not exist (box in the
above example). Whereas the addition of a fallback preset guarantees a
result that works.
Note that I am defining the cursory-fallback-preset using defconst
instead of exposing it as a user option. This is to avoid a scenario
where the values are accidentally set incorrectly.
:cursor-colorThis is a new attribute which corresponds to the background value of
the cursor face.
When the value is nil or unspecified (the default), Cursory does
not modify the cursor face.
When the value is a hexadecimal RGB color value, like #123456 it
is used as-is. Same if it is a named color among those produced by
the command list-colors-display.
When the value is the symbol of a face (unquoted), then the
foreground of that face is used for the cursor face, falling back
to default.
Concretely, users can have something like this in their configuration:
(setq cursory-presets
'((box
:cursor-color "#21439f"
:blink-cursor-interval 1.2)
(baring
:cursor-type (bar . 2)
:cursor-color error ; the `error' face will typically be red (see `list-faces-display')
:blink-cursor-interval 0.8)
(underscore
:cursor-color "green" ; see `list-colors-display'
:cursor-type (hbar . 1)
:blink-cursor-interval 0.3
:blink-cursor-blinks 50)
(t ; the default values
:cursor-color unspecified ; use the theme's original
:cursor-type box
:cursor-in-non-selected-windows hollow
:blink-cursor-mode 1
:blink-cursor-blinks 10
:blink-cursor-interval 0.2
:blink-cursor-delay 0.2)))
Remember to read the documentation of cursory-presets for all the rest.
cursory-mode persists the :cursor-color while changing themesBefore, the cursory-mode would only take care to save the last
selected preset and to persist it across Emacs sessions. In addition
to that, it now also ensures that loading a new theme does not
override the :cursor-color.
cursory-set-preset prompt is smarter about its default valueWhen the cursory-set-preset is called interactively, it uses the
minibuffer to prompt for a preset among the cursory-presets.
In the past, its default value would simply be the last selected
preset. Cursory would not check whether that symbol was still a member
of the cursory-presets. This had the potential to set the wrong
configurations.
Now the prompt only uses as its default value the last selected and existing preset among those found in the history of selections. It will not provide a default if it cannot find any.
Note that the “default value” in the context of the minibuffer refers
to the input that will normally be provided if the user types RET
without writing anything into the minibuffer.
dired-previewBelow are the release notes
This is a small release that provides quality-of-life refinements.
The new user option dired-preview-trigger-on-start controls whether
a preview is produced automatically when (i) entering a directory
while (ii) the dired-preview-mode or its global counterpart is
enabled.
The default value is non-nil, which preserves the behaviour we have
always had of previewing outright. When set to nil, the preview does
not happen upon entering a directory and is triggered only after one
of the commands in denote-preview-trigger-commands is invoked.
I did this is in response to issue 31 by dasoju: https://github.com/protesilaos/dired-preview/issues/31.
The denote-preview-trigger-commands is now declared as a “user
option” rather than a generic variable, meaning that users are
encouraged to customise it (and it technically is available via the
Custom interface and related). Everything should otherwise work the
same as before.
dired-dwim-targetPrevious versions would make dired-dwim-target not return the
directory of the other window. Whereas we want the preview to not
influence how Dired behaves when copying or renaming files.
The bug was addressed in patch release 0.5.2.
dired-preview-page-upIt was missing the macro we define to perform operations in the preview window.
Thanks to Alex Popescu for telling me that the command was not working. This was done in issue 28: https://github.com/protesilaos/dired-preview/issues/28.
The bug was addressed in patch release 0.5.1.
In this ~13-minute video I explain how to use the Denote user option
denote-file-name-components-order. Then I show how to retroactively
rename all the files that have the Denote file-naming scheme so that
they follow the desired order. In the process, I cover the command
denote-sort-dired (alias denote-dired), which helps produce a flat
listing of Denote files, even if they exist in subdirectories of the
denote-directory.
The code used in this video:
;; The default:
(setq denote-file-name-components-order '(identifier signature title keywords))
;; Any order will work. Here is the one I am using for this demonstation:
(setq denote-file-name-components-order '(identifier signature keywords title))
;; And here is another:
(setq denote-file-name-components-order '(identifier keywords title signature))
;; And yet another one for the sake of completeness:
(setq denote-file-name-components-order '(title keywords signature identifier))
(defun prot/denote-rename-all-to-reorder-components ()
"Call `denote-dired-rename-files' without any prompts.
In other words, preserve the value of each Denote file name component.
Use this command if you wish to modify the user option
`denote-file-name-components-order' and then want your existing Denote
files to retroactively follow that order."
(interactive)
(let ((denote-prompts nil))
(call-interactively 'denote-dired-rename-files)))
Live completions to update the results as you type.
Passlist and blocklist of commands or completion categories to
control whether the *Completions* buffer shows up eagerly.
Other relevant options to control when the *Completions* are
displayed.
Per command or completion category sorting methods.
A cleaner *Completions* buffer, optionally without a mode line.
Commands to cycle from the minibuffer to the *Completions* and
vice versa.
In essence, MCT is (i) a layer of interactivity on top of the out-of-the-box completion experience, and (ii) glue code that combines built-in functionalities to make the default completion framework work like that of more featureful third-party options.
mctBelow are the release notes.
This version contains several refinements to an already stable package.
The new user option mct-sort-by-command-or-category determines how
completion candidates are sorted.
This is an alist where each element is of the form (SYMBOLS . SORT-FUNCTION).
SYMBOLS is either a symbol or a list of symbols. SYMBOLS can refer
to the symbol of a function or completion category. It can also be t,
which refers to the fallback value.
SORT-FUNCTION is a function that takes a list of strings and returns a
list of strings, sorting them accordingly. Examples of a SORT-FUNCTION
are:
mct-sort-by-alphamct-sort-by-alpha-then-by-lengthmct-sort-by-historymct-sort-by-directory-then-by-fileTo not perform any sorting on the completion candidates that match
SYMBOLS set SORT-FUNCTION to nil.
If there are conflicting configurations between a SYMBOLS function and
completion category, then the function takes precedence (for example
find-file is set to sort directories first whereas the file
completion category is set to sort by history).
completing-read-multipleUsers of Emacs prior to version 31 do not have a built-in way to
inform them when they are dealing with a completing-read-multiple
minibuffer prompt. Emacs 31 introduces the user option crm-prompt
and its related functionality to tell the user what they need to know.
The mct-completing-read-multiple-indicator can be used in the
meantime to achieve the same results. It is set to a non-nil value by
default. Opt out by changing it to nil.
*Completions* are persistent when neededThis concerns the case where a command or completion category is part
of the mct-completion-passlist or when the user option
mct-live-completion is set to non-nil or visible. In such cases,
the completion candidates are on display and we want to keep them
there while performing a dynamic completion, such as with the
find-file command (“dynamic” in the sense that it returns a new list
of candidates to match the current path).
Persisting the *Completions* means that as we narrow the list, we
still see all the matching results.
In the past, we provided the option mct-persist-dynamic-completion
though we do not need it anymore.
I was inspired to make this change in response to a question posed by
Ryan Davis regarding the behaviour of mct-persist-dynamic-completion.
This was done in issue 7: https://github.com/protesilaos/mct/issues/7.
Vertical alignment of the *Completions* buffer is more precise.
Thanks to Jessie Hu for the contribution in pull request 6:
https://github.com/protesilaos/mct/pull/6.
The command mct-choose-completion-exit no longer tries to expand
further. In the previous implementation it would try to match the
last known selection from the history when using a file prompt. For
example we have this workflow:
/path/to/file/ and exit./pa, the completions pop up, we select /path and
invoke mct-choose-completion-exit. This wrongly expands into
/path/foo/ instead of taking just /path (given that it has no
further input to determine an extension of that string).Now the command will do the right thing based on the user’s input.
If you want maximalist themes in terms of colour, check my ef-themes
package. For something in-between, which I would consider the best
“default theme” for a text editor, opt for my modus-themes.
doric-themesBelow are the release notes.
This version introduces several refinements to the individual themes comprising the Doric themes collection. Changes pertain to the intensity of colours in use. While each tweak is small, their cumulative effect contributes to a more consistent design.
Users who are interested in the contrast ratio values and relevant
colour distance can refer to the contrasts.org file that is part of
the project’s Git repository: https://github.com/protesilaos/doric-themes.
I extended support for several faces or face groups including:
ace-windowdictionaryembark-keybindingmanmarkdown-metadata-key-facepackage-mark-delete-linepackage-mark-install-lineread-multiple-choice-facercircspacious-paddingtextsec-suspiciouswhich-key-key-facewomanSome other faces that were already covered are revised in the interest of theme-wide consistency. Again, changes are subtle.
The colour of the directory icons in all-the-icons and nerd-icons
packages is toned down a little bit to avoid exaggerations.
Several spacing-sensitive faces, like Org tables and code blocks,
unconditionally inherit the fixed-pitch face. This means that, in
principle, they are rendered in a monospaced font even when the user
activates variable-pitch-mode or sets the default face to a
proportionately spaced typeface. Users who need to specify the
applicable font family can either use the following, mutatis mutandis,
or take a look at my fontaine package:
(set-face-attribute 'default nil :family "Aporetic Sans Mono" :height 160)
(set-face-attribute 'variable-pitch nil :family "Aporetic Sans" :height 1.0)
(set-face-attribute 'fixed-pitch nil :family "Aporetic Sans Mono" :height 1.0)
Other theme packages of mine have an option for “mixed fonts” (like
modus-themes-mixed-fonts), but I think I will not be providing
options for the Doric themes. At least not for the time being while I
explore the design space for minimalist themes.
beframe enables a frame-oriented Emacs workflow where each frame has
access only to the list of buffers visited therein. In the interest of
brevity, we call buffers that belong to frames “beframed”.
beframeBelow are the release notes
This version adds some minor improvements to a stable package.
The built-in Xref mechanism is typically used to navigate to the
definition of the symbol at point in a programming major mode. The
command xref-find-definitions (M-. by default) jumps to the
source, while xref-go-back (M-, by default) goes back in the
history of visited positions until it reaches the starting point.
When beframe-mode is enabled, each new frame has its own Xref
history. This means that finding a definition in one frame does not
interfere with the Xref history of another frame.
When beframe-mode is enabled, it sets the standard
read-buffer-function to one that filters buffers by frame. Any
command that uses that, such as switch-to-buffer (C-x b by
default) is thus beframing its completion candidates.
Such prompts get a prefix to inform the user of their behaviour. By
default this is [Beframed]: it is subject to the user option beframe-prompt-prefix.
Users who do not wish to have any prefix can set this option to nil or
an empty string.
When there is a string, it is styled with the face beframe-face-prompt-prefix.
modus-themes (also built into Emacs 28+)This is a small release that corrects a mistake I made in the previous version. It also introduces some minor refinements.
In version 4.7.0, I made the mistake of merging some stylistic
tweaks to show-paren-mode that I was experimenting with. The idea
was to get a feel for how the subtle colouration of matching
delimiters affects the usability of the themes. In short, it is not a
good default for our purposes (though users have the option to
override the applicable colours, as explained in the Modus themes
manual).
Thanks to Morgan Willcock for reporting the unwanted change in issue 139: https://github.com/protesilaos/modus-themes/issues/139.
bg-paren-match of modus-vivendi-tinted is a bit greenerWe go from #5f789f to #4f7f9f. The latter fits better with the
rest of the theme.
This concerns message.el and anything building on top of it, like
Gnus, Mu4e, and Notmuch. I made modus-operandi-tinted and
modus-vivendi-tinted use two colours that are more in line with the
established patterns of their respective theme. The changes are small,
but contribute to a more consistent experience.
property semantic mapping is availableBy default, it uses the same colour as the variable. Users who want
to have more refined colouration in supported modes (typically
involving tree-sitter), can change the relevant “palette overrides”
user option we provide, such as modus-themes-common-palette-overrides
or modus-operandi-palette-overrides and related.
Thanks to Alexandr Semenov for requesting this in issue 141: https://github.com/protesilaos/modus-themes/issues/141.
tmr package is now supportedIts faces were already consistent with the Modus themes, though now I cover them at the theme level to subject them to palette overrides.
Those are only ever seen if the user configures Gnus in a certain way and follows a specific workflow. They now get a subtle foreground value. This is in response to the issue 119 by sivaramn: https://github.com/protesilaos/modus-themes/issues/119.
modus-themes-rotate command can now go backwardsWhen called with a prefix argument (C-u by default), this command
will rotate the modus-themes-to-rotate from right to left.
Otherwise, it goes from left to right.
Thanks to Jacob S. Gordon for the contribution. It was sent as a patch as part of issue 143: https://github.com/protesilaos/modus-themes/issues/143.
]]>spacious-padding-mode work as
intended when used in tandem with the Emacs daemon and subsequent
calls to emacsclient -c. I made the function responsible for
triggering the “spacious padding” effects work with individual frames
and then I responded to issue 33 by Lou Woell about integrating that
with the server-after-make-frame-hook: https://github.com/protesilaos/spacious-padding/issues/33.
Additionally, the package now defines two faces that can be used to
configure the user option spacious-padding-subtle-mode-line (read
its documentation string for all the possible values it accepts).
Here is how they can be set (default value is nil):
(setq spacious-padding-subtle-mode-line
'( :mode-line-active spacious-padding-subtle-mode-line-active
:mode-line-inactive spacious-padding-subtle-mode-line-inactive))
Reload the spacious-padding-mode for changes to take effect.
When configured this way and with default styles they make the mode line use a minimalist overline with no background colour. The active mode line has a more noticeable border than the inactive ones. All my themes are designed to support this aesthetic (though themes can style those faces as they see fit).
This package provides a global minor mode to increase the
spacing/padding of Emacs windows and frames. The idea is to make
editing and reading feel more comfortable. Enable the mode with M-x
spacious-padding-mode. Adjust the exact spacing values by modifying
the user option spacious-padding-widths.
Inspiration for this package comes from Nicolas Rougier’s impressive
designs and Daniel Mendler’s
org-modern package.
spacious-paddingIf you want maximalist themes in terms of colour, check my ef-themes
package. For something in-between, which I would consider the best
“default theme” for a text editor, opt for my modus-themes.
doric-themesBelow are sample screen shots. The typefaces on display come from my Aporetic fonts.
At 11:00 AM Europe/Athens, I will start streaming my work on some of the Emacs packages I have authored and maintain. I will engage with the chat, if there are any comments, but my focus will be on the code.
The packages I plan to cover are denote, denote-org, denote-sequence, doric-themes. Depending on how it goes, I will work on more of my packages.
The video will be recorded and can be watched later.
All my Emacs-related work: https://protesilaos.com/emacs.
]]>doric-themes is my new in-development package for Emacs. It is a set of themes that conform with a minimalist aesthetic: they use few colours and appear monochromatic in many contexts. Below are the screen shots.
In terms of my overall theme work for Emacs, the doric-themes are the most minimalist, ef-themes the most maximalist, and the modus-themes remain what I consider the best “default theme” style. All of them are designed to be highly legible.
I plan to add doric-themes to GNU ELPA within the next days. For the time being, they are only available from source code: https://github.com/protesilaos/doric-themes.
Finally, the backronym for “Doric” is “Doric Only Really Intensifies Conservatively”.
Enjoy your new theme!
On Sunday, the 11th of May 2025, at 14:00 o’clock of time zone Europe/Athens, I will do a live stream where I will answer every question posted in the chat. The idea is to cover any of the topics I already write/talk about on my website, including Emacs, my recently published “Emacs Lisp Elements” free book, libre software, politics, philosophy, and everyday affairs.
I will answer every question from top to bottom to the best of my abilities and will not give any of those formulaic non-answers. No tricks; no gimmicks!
The plan is to do a minimum of two hours, but may extend it for at least one or two more hours depending on the participation in the chat.
The event will be recorded: no worries if you cannot make it live.
Talk to all of you soon!
]]>ef-themes are a collection of light and dark themes for GNU
Emacs that provide colourful (“pretty”) yet legible options for users
who want something with a bit more flair than the modus-themes (also
designed by me).
ef-themesBelow are the release notes.
This version introduces minor refinements to an already stable package.
The commands ef-themes-preview-colors and ef-themes-preview-colors-current
produce a preview of the given theme’s palette. In the past, we were
using a bespoke buffer for this task, just how the built-in command
list-colors-display does it.
Now we rely on the built-in tabulated-list-mode to get a cleaner
tabulated view. Plus, users can sort by column.
Added support for my tmr package. This will be especially
noticeable in its tabulated view (used to show timers, with the
command tmr-tabulated-view (alias tmr-list-timers)).
Added explicit support for my spacious-padding package.
Specifically, this is for the faces spacious-padding-subtle-mode-line-active
and spacious-padding-subtle-mode-line-inactive. Those can be
configured as part of the user option spacious-padding-subtle-mode-line.
(setq spacious-padding-subtle-mode-line
'( :mode-line-active spacious-padding-subtle-mode-line-active
:mode-line-inactive spacious-padding-subtle-mode-line-inactive))
Made the helpful headings use whatever the style of level 1
headings is, as defined by the user option ef-themes-heading.
Thanks to John Haman for applying the relevant code we have for the
modus-themes. The change is small (and comes from my modus-themes,
anyway), meaning that John does not need to assign copyright to the
Free Software Foundation.
Added support for the howm package.
Extended support for the auto-dim-other-buffers package to include
its auto-dim-other-buffers-hide-face.
Made sure that all new transient faces conform with the design
priorities of the themes. Concretely, this means that they do not
support any colour-coding: all keys look the same, regardless of
whether they mean “continue”, “exit”, or anything else.
Colour-coding with a full spectrum of colours cannot be accessible
(and I do not believe colour-coding alone even works because the
colours have nothing else to be associated with, like how a red line
in a diff buffer also goes together with the minus sign).
Thanks to Kevin Fleming for including the transient-key-stack face
that I had originally missed. This was done in pull request 54:
https://github.com/protesilaos/ef-themes/pull/54. The change is
small, meaning that Kevin does not need to assign copyright to the
Free Software Foundation
Extended support for adoc-mode courtesy of Leilei332. This was
done in pull request 52: https://github.com/protesilaos/ef-themes/pull/52.
The change is within the ~15-line limit, meaning that its author does not need to assign copyright to the Free Software Foundation.
Below are the release notes.
The major change for this release is that Aporetic Sans Mono and
Aporetic Serif Mono are rendered in a strictly monospaced width. This
means that characters such as the em dash (—) occupy the same space
as the regular dash.
Before, Aporetic Sans Mono and Aporetic Serif Mono had some characters that were proportionately spaced. This would upset the expectations of terminal emulators and thus break the display, sometimes with characters overlapping when they should not.
Other changes are more subtle. They pertain to the style of individual characters, namely:
The Greek lower case lambda (λ) has a flat top instead of a straight
one. The legs remain straight. This makes it consistent with the
design of the Greek lower case delta (δ), among others.
The Greek lower case mee (μ) has a rounder right corner, which is
in line with a whole range of characters.
The micro sign is the same design as the Greek lower case mee, even though these are technically two distinct code points.
The at sign (@) of Aporetic Sans and Aporetic Serif (i.e. the
proportionately spaced fonts I provide) is a bit taller than it was
before. This makes it look more related to its monospaced
counterpart, which is also relatively tall.
I also added support for some more Greek characters, as well as the
Latin eth (ð), thorn (þ), and thorn capital (Þ).
All other changes are done to retain the aesthetic of the fonts while dealing with the breaking changes introduced by the upstream Iosevka project.
]]>Sources:
show-fontBelow are the release notes.
This version adds some refinements to an already stable package.
show-font-list is an alias for show-font-tabulatedThe show-font-list command was using a custom buffer that listed
font families and their corresponding short preview. It did not have
any other feature.
I made changes under the hood to rely on the built-in
tabulated-list-mode which is a standard and gives us the option to
sort by column. The show-font-list is thus an alias for the new
command show-font-tabulated. Right now the sorting facility only
applies to reversing the name-based order. In the future we may have
more columns, such as if we describe a font as “Latin”, “Greek”, etc.
In the past, the font listing would include families that could not
display the show-font-pangram or, indeed, any Latin character. Those
would be rendered as empty boxes.
I have now introduced a simple heuristic to test that the given family supports Latin characters. If it does not, then (i) it is highlighted with a different colour, (ii) it shows “No preview” instead of the pangram, and (iii) it displays the information in the Emacs default font family. Some families do not play nice with this approach though, as they pass the test but still do not display any Latin characters. This happens with icon fonts.
The long-term goal is to support different scripts and show the appropriate text for each of them.
show-font-sentences-sample adds more to the show-font-select-previewThe new user option show-font-sentences-sample is a list of strings
that can be used to exhibit common patterns and letter combinations.
The default value is carefully designed to show if a font family is
stylistically consistent, such as with how it draws i, l, t, or
h, n, m, and so on. Plus, it teaches you some obscure words like
“scholarch”, “antipode”, and “heteroclite”: use them with your Greek
friends—and if they do not know those words, then they must buy you
a café frappé!
show-font-title-small is an obsolete alias for the more
appropriately named show-font-title-in-listing.show-font-select-preview now
correctly uses its own history and default value.show-font-character-sample
includes some more patterns to better test the adequacy of a font
family. This sample is displayed in the buffer produced by the
command show-font-select-preview.tmrBelow are the release notes.
This version makes small refinements to an already stable package.
tmr-tabulated.el is part of tmr.elThe command tmr-tabulated-view, which produces a grid with
timers+descriptions, used to be in a separate file. It now is part of
the singular tmr.el to keep things simple. Users who were using
(require 'tmr-tabulated) or similar will now get a warning. Simply
load tmr instead.
tmr-tabulated-view commandWhen the command tmr-tabulated-view (alias tmr-list-timers) is
called interactively, it uses the *tmr-tabulated-view* buffer just
as it did before. Though it also evaluates the new user option
tmr-list-timers-action-alist: it is a variable that controls where
the buffer is displayed. The default value displays the buffer at the
bottom of the Emacs frame and makes some other tweaks for usability.
Watch my video on the display-buffer-alist for further details on
how to control the display of buffers: https://protesilaos.com/codelog/2024-02-08-emacs-window-rules-display-buffer-alist/.
The tmr-tabulated-view command is further revised to make it
callable from a program. One scenario where we do this is to interrupt
the termination of Emacs if there are running timers (more below).
In the past, we did not have anything to prevent the termination of
Emacs if timers were running: Emacs would simply shut down. Now we
define the tmr-kill-emacs-query-function, which is added to the
standard kill-emacs-query-functions: if there are running timers, it
asks for confirmation before closing Emacs. To make it easier for
users to decide how to proceed, it also pops up the list with all the
timers (i.e. it uses tmr-tabulated-view from Lisp, as noted above).
The buffer produced by tmr-tabulated-view now uses more colours to
make it easier to track the data it presents. These are all the faces
it applies:
tmr-tabulated-start-time: The time when the timer was started.
tmr-tabulated-end-time: The time when the timer is set to end.
tmr-tabulated-remaining-time: The remaining time.
tmr-tabulated-acknowledgement: Whether the timer needs to be
“acknowledged” after it ends (if it is marked as “acknowledged”,
then it will not go away until the user confirms they have seen it).
tmr-tabulated-description: The text describing what the timer is
about.
modus-themes (also built into Emacs 28+)This release introduces many subtle stylistic tweaks to the “tinted”, “deuteranopia”, and “tritanopia” theme variants.
modus-themes-list-colors command uses a tabulated listThis command and its modus-themes-list-colors-current variant help
users see the colour values and semantic palette mappings defined by
the given theme. In the past, their buffer was designed in the same
spirit as that of the command list-faces-display, whereas now it is
like the buffer of the command list-packages. Concretely, users may
now sort by column. Do M-x describe-mode while in that buffer to
learn about the available commands and their respective key bindings.
The overall feel of the modus-operandi-tinted and modus-vivendi-tinted
themes is the same as before. Though in a side-by-side comparison between the
old and new versions reveals lots of subtle differences. The general
idea is to make the themes a bit more consistent by tweaking the
foreground values to be more harmonious in combination with their
background.
These are the modus-operandi-deuteranopia and modus-vivendi-deuteranopia,
which are optimised for users with red-green colour deficiency. In the
past, these themes used blue and yellow hues wherever a concept of
“success” versus “failure” had to be established. This approach is
more generalised now, to include programming syntax highlighting and
many other contexts. In short, the themes are more blue+yellow, while
retaining their original feel.
As above, the modus-operandi-tritanopia and modus-vivendi-tritanopia
themes, which are optimised for users with blue-yellow colour deficiency,
use a red+cyan palette in more places. Overall, they feel like they
did before, only they are more consistent.
Extended support for the icomplete faces that are coming in Emacs
version 31.
Added support for treemacs faces, courtesy of Rahul Juliato in
pull request 121: https://github.com/protesilaos/modus-themes/pull/121.
Rahul has assigned copyright to the Free Software Foundation.
Added support for the tldr package.
Extended support for adoc-mode. Thanks to Leilei332 for the
contribution in pull request 137: https://github.com/protesilaos/modus-themes/pull/137.
The change is within the ~15-line limit, meaning that the author
does not need to assign copyright to the Free Software Foundation.
Added support for my spacious-padding package, specifically the
faces it can use when the spacious-padding-subtle-mode-line user
option is enabled.
Added support for the howm package.
Extended support to the new faces of the transient package. More
specifically, all those faces use the same colour for key bindings
because the idea of colour coding keys (e.g. light yellow means
something different than dark blue) does not work in practice when
considering accessibility. Such semantics should not be limited to
differences in colour: they should also have distinct indicators,
such as ASCII or Unicode characters.
Revised the avy package’s faces to only use one coloured
background. The multiple coloured backgrounds have been a perennial
problem for our accessibility requirements and have made the themes
needlessly more complex just to support an edge case. With this
simplified style, avy continues to work fine: it simply is less
flamboyant. Other interfaces with avy-like model of interaction,
such as optional extensions to the vertico and corfu packages,
have these same changes, in the interest of consistency.
Update the meow sample configuration in the manual. This package
is not directly supported at the theme level because (i) I do not
use it and (ii) it is very hard for an outsider to it to trigger the
display of all of its faces in the right context. Without seeing how
all of them look together, I cannot come up with a reliable design.
The manual offers a “good enough” approximation.
Broadened the support of the vterm faces to include the “bright”
colours, while updating those that were already covered. Thanks to
Edgar Vincent for informing me that some of the vterm faces were
changed a while ago. This was done in issue 317 on the GitLab
mirror: https://gitlab.com/protesilaos/modus-themes/-/issues/317.
Revised the org-column-title face to inherit the fixed-pitch
face if the user option modus-themes-mixed-fonts is non-nil. This
user option makes it possible to have a buffer with proportionately
spaced fonts (such as by enabling variable-pitch-mode), while
keeping spacing-sensitive elements, like tables and code blocks, in a
monospaced font.
Thanks to pedro-nonfree for bringing this matter to my attention in issue 129: https://github.com/protesilaos/modus-themes/issues/129.
Simplified the helper function modus-themes--retrieve-palette-value
to make it more efficiently. Thanks to Basil L. Contovounesios for
the contribution in merge request 60 on the GitLab mirror:
https://gitlab.com/protesilaos/modus-themes/-/merge_requests/60.
Reworded the minibuffer prompt of the modus-themes-list-colors
command.
Made Ivy and IDO subdirectories and “virtual” buffers easier to tell apart from matching text highlights.
Included coverage for the auto-dim-other-buffers-hide-face of the
package auto-dim-other-buffers
Covered the built-in abbrev-table-name face.
Denote is based on the idea that files should follow a predictable and descriptive file-naming scheme. The file name must offer a clear indication of what the contents are about, without reference to any other metadata. Denote basically streamlines the creation of such files or file names while providing facilities to link between them (where those files are editable).
Denote’s file-naming scheme is not limited to “notes”. It can be used for all types of file, including those that are not editable in Emacs, such as videos. Naming files in a constistent way makes their filtering and retrieval considerably easier. Denote provides relevant facilities to rename files, regardless of file type.
denoteBelow are the release notes.
This is a massive release. There is one breaking change, which should
be easy to adapt to: this pertains to the reorganisation of the
project to separate the “core” of Denote from its “extensions”. The
core is the denote package. Each extension now has its own package
(details below).
Other than that, this version includes lots of new features for
searching and linking as well as quality-of-life refinements. We have
generalised the infrastructure for performing queries in the
denote-directory and made the buffers with the search results more
useful.
Take your time to read through this publication. I am writing it for you. Also remember that the most up-to-date resource for anything related to Denote is its manual. You are always welcome to contact me: https://protesilaos.com/contact. Or join the development on the Git repository.
As usual, special thanks to Jean-Philippe Gagné Guay for making high quality contributions to Denote since the beginning of the project ~3 years ago. Those will not always be headline features, but are important improvements to the underlying code base.
I mention contributions from Jean-Philippe and others in its context. Though I do not cover implementation details, otherwise this document will be the size of a book. This does not mean that they are no important though. Please consult the Git commit log for all the technicalities.
In previous versions of Denote, we included some optional extensions
as part of the denote package. These included the files
denote-org-extras.el (Org dynamic blocks, among others),
denote-journal-extras.el (streamlined for journaling),
denote-silo-extras.el (working with multiple Denote silos).
The files denote-md-extras.el (Markdown extras) and
denote-sequence.el (sequence notes, including Luhmann-style
alphanumeric sequences) were also part of the project during the last
development cycle, though they never made it into a tagged release.
All these are now available as standalone packages on the official GNU ELPA archive:
denote-org: In the Emacs configuration file, replace all
instances of denote-org-extras with denote-org.
denote-journal: Replace denote-journal-extras with denote-journal.
denote-silo: Replace denote-silo-extras with denote-silo.
denote-markdown : Replace denote-md-extras with denote-markdown.
denote-sequence: No changes to any of the defined symbols.
Simply get the new package.
I will document each of these packages further below. The plan, going forward, is to maintain all the packages and coordinate their new versions.
While the extras are moved out to their own code repositories, all
other features are merged into denote.el. Those include everything
that was in denote-sort.el and denote-rename-buffer.el.
The “sort” mechanism is mostly for package developers. We use it
extensively in our Org dynamic blocks, which are now part of the
denote-org package.
The denote-dired command (alias denote-sort-dired) is the only
user-facing “sort” command we have always provided. It produces a
fully fledged Dired buffer showing the results of the given search
for file names. The matching files are sorted according to the
user’s expressed preference. The details are described in the
manual.
The denote-rename-buffer-mode and all of its user options are
unchanged. This mode automatically renames the buffer of a given
Denote file so that it is easier to read it. Again, the manual
covers the technicalities.
Users do not need to make changes, unless they are explicitly loading
denote-sort-dired and denote-rename-buffer. In that case, they may
just remove those calls: only denote needs to be loaded.
denote-query-modeMany of the features I will describe below produce search results via
the built-in Xref mechanism. Xref performs a search with a Grep or
Grep-like program, subject to the user option xref-search-program.
The buffer those search results are displayed in runs the
denote-query-mode. It supersedes denote-backlinks-mode.
The denote-query-mode supports the following:
TAB, just how it is done in Org buffers.C-h m
(describe-mode) to learn about all the relevant commands.We have had support for Xref since the original version of Denote. It
now is more generalised to cover backlinks, query links, and
denote-grep (more below).
Denote has always provided the option to link directly to a file with
a given name by referencing its identifier. This can be done with the
command denote-link, among a few others like it (always consult the
manual of Denote).
In addition to these “direct links”, we also support “query links”. Those do not point to a file but instead trigger a search. The results are placed in a buffer that uses the appropriate major mode.
There are two types of query links:
Query file contents: Use the command denote-query-contents-link
to insert a query link at point for “file contents”. It perform a
search inside files in the denote-directory and put the results in
a denote-query-mode buffer.
Query file names: Use the denote-query-filenames-link to insert
a query link for “file names”. It performs the query against file
names (not contents!) and puts the results in a dired buffer.
The display of the buffer with the query link results is controlled by
the user option denote-query-links-display-buffer-action.
Query links are styled a little bit differently than direct links.
Compare the denote-faces-link with denote-faces-query-link. Both
should look okay with most themes.
Denote query links are supported as part of the denote: hyperlink
type. They are available in all file types we define (per the user
option denote-file-type) and should, in principle, work in any
custom file type (advanced users can check the variable denote-file-types).
In the past, the command denote-backlinks would produce a bespoke
buffer showing a list of file names that included links to the current
file (any file with the Denote file-naming scheme can have backlinks,
by the way, including PDFs, videos, etc.). This buffer did not provide
any additional functionality. We used to support the option to show
results in their context via denote-backlinks-show-context. Those
would be rendered in a standard Xref buffer.
The contextual results are now the default and sole option. This is
because we have expanded the functionality of those buffers to use the
denote-query-mode, as explained above. Plus, it makes our code base
simpler.
Users will notice how backlikns look just like a query link for file contents. This is because backlinks are the original query links since day one of Denote.
The command denote-link-to-file-with-contents allows users to
produce a direct link to a file whose contents (not file name!)
includes the given query.
Similarly, the command denote-link-to-all-files-with-contents
generates a typographic list (bullet list) to all files whose contents
match the given query.
The manual covers all linking commands in depth.
denote-search is part of denoteThe denote-search package by Lucas Quintana uses the infrastructure
of Denote to perform searches in file contents. We now provide its
feature set as part of core denote.
We decided to do this since query links already introduced all of the
requisite generalisations to denote-query-mode.
Users can rely on the commands denote-grep, denote-grep-marked-dired-files,
and denote-grep-files-referenced-in-region.
The placement of these buffers is subject to the user option
denote-grep-display-buffer-action.
This functionality was introduced in two pull requests by Lucas Quintana, 571 and 573, with further changes by me:
Lucas has assigned copyright to the Free Software Foundation.
I think this was a much-needed addition to the core of Denote. It
complements denote-dired and query links.
denote-link-description-formatThe old user option denote-link-description-function is deprecated
and superseded by the new denote-link-description-format. The new
user option still accepts a custom function as its value, so the old
behaviour should be retained.
What the new denote-link-description-format supports is an easier
way to customise the description of a link by using format specifiers
for common options. For example, users who only want to see the title
of the linked file can do this:
(setq denote-link-description-format "%t")
The documentation of this user option covers all the format specifiers and further details.
The command denote-add-front-matter is superseded by
denote-rename-file and related. Those renaming commands will add
missing front matter or rewrite the modified lines of existing front
matter. This is due to refinements made by Jean-Philippe Gagné Guay
to the file renaming mechanism. We discussed this deprecation in
issue 498: https://github.com/protesilaos/denote/issues/498. Also
thanks to Samuel Flint for reporting an earlier problem with file
name signatures: https://github.com/protesilaos/denote/issues/492.
The user option denote-open-link-function specifies the function
used by Denote to open the file of a direct link.
The user option denote-org-store-link-to-heading can now be set to
form generic context links without a PROPERTIES drawer and
corresponding CUSTOM_ID. Set the value of this variable to
'context. Read its documentation for further details.
Also about denote-org-store-link-to-heading, we have changed its
default value to nil, which is what we were doing for most of
Denote’s history. This means that, by default, org-store-link and
anything building on top of it will create a link only to the
current Denote file, like denote:IDENTIFIER, but not to the
current heading within that file. To create links to the
file+heading, set the value of this variable to 'id.
The command denote-dired-link-marked-notes is an alias for
denote-link-dired-marked-notes.
The user option denote-sort-dired-extra-prompts control what
denote-dired (alias denote-sort-dired) prompts for. It accepts
either a nil value or a list of symbols among sort-by-component,
reverse-sort, and exclude-regexp. The order those symbols appear
in the list is significant, with the leftmost coming first.
There is a new denote-sort-identifier-comparison-function variable
which determines how identifier-based sorting should be done by
default. It complements the existing denote-sort-title-comparison-function,
denote-sort-keywords-comparison-function, denote-sort-signature-comparison-function.
Thanks to Maikol Solís for the contribution in pull request 517:
https://github.com/protesilaos/denote/pull/517. The change is
small, meaning that Maikol does not need to assign copyright to the
Free Software Foundation (though I believe the paperwork is done, anyway).
Lots of refinements to the doc strings of individual variables and/or functions as well as the manual.
Lots of other contributions to discussions and questions on the Git repository. Granted, these are not “changes” per se but are part of the development effort nonetheless.
Made denote-get-path-by-id use denote-get-file-extension-sans-encryption
instead of denote-get-file-extension. This fixes a bug where the
extension is duplicated if it has an encryption component. Thanks to
eum3l for the patch in pull request 562: https://github.com/protesilaos/denote/pull/562.
The change is small, meaning that the author does not need to assign
copyright to the Free Software Foundation.
Same as above for denote--rename-file, which was done in pull
request 557: https://github.com/protesilaos/denote/pull/557.
The following have been added or modified.
NEW Function denote-file-has-denoted-filename-p: Return non-nil
if FILE respects the file-naming scheme of Denote. This tests the
rules of Denote’s file-naming scheme. Sluggification is ignored. It
is done by removing all file name components and validating what
remains. Thanks to Jean-Philippe Gagné Guay for the pull request
515: https://github.com/protesilaos/denote/pull/515.
NEW Functions denote-infer-keywords-from-files: Return list of
keywords in denote-directory-files. With optional
FILES-MATCHING-REGEXP, only extract keywords from the matching
files. Otherwise, do it for all files. Keep any duplicates. Users
who do not want duplicates should refer to the functions
denote-keywords.
MODIFIED Function denote-keywords: Returns an appropriate list
of keyword candidates, while accounting for the value of the user
option denote-infer-keywords. It now also accepts the optional
FILES-MATCHING-REGEXP parameter.
MODIFIED Function denote-directory-files: Returns a list of
absolute file paths in variable denote-directory. It now accepts
the optional EXCLUDE-REGEXP parameter.
MODIFIED Function denote-format-file-name: Formats a file name.
The way it treats its ID parameter has changed. Please read its
doc string. Thanks to Jean-Philippe Gagné Guay for the pull request
496: https://github.com/protesilaos/denote/pull/496.
ALIAS Function denote-retrieve-filename-keywords-as-list: This
is a name that is easier to discover than denote-extract-keywords-from-path,
because of the many other functions with the denote-retrieve-* prefix.
MODIFIED Function denote-retrieve-filename-identifier: Extracts
the identifier from FILE name, if present, else returns nil. To
create a new one from a date, refer to the denote-get-identifier
function. Thanks to Jean-Philippe Gagné Guay for the pull request
476: https://github.com/protesilaos/denote/pull/476.
MODIFIED Function denote-get-identifier: Converts DATE into a
Denote identifier using denote-id-format. If DATE is nil, it
returns an empty string as the identifier. Also by Jean-Philippe in
pull request 476 mentioned right above.
MODIFIED Function denote-date-prompt: Prompts for a date,
expecting YYYY-MM-DD or that plus HH:MM (or even HH:MM:SS).
Can also use Org’s more advanced date selection utility if the user
option denote-date-prompt-use-org-read-date is non-nil. It now has
the optional parameters INITIAL-DATE and PROMPT-TEXT. Thanks to
Jean-Philippe Gagné Guay for the pull request 576:
https://github.com/protesilaos/denote/pull/576.
NEW Function denote-retrieve-groups-xref-query: Accesses the
location of xrefs for QUERY and group them per file. Limit the
search to text files.
NEW Function denote-retrieve-files-xref-query: Returns sorted,
deduplicated file names with matches for QUERY in their contents.
Limits the search to text files.
NEW Function denote-retrieve-xref-alist: Returns xref alist of
files with the location of matches for QUERY. With optional
FILES-MATCHING-REGEXP, it limits the list of files accordingly
(per denote-directory-files). At all times, it limits the search
to text files.
NEW Function denote-prepend-front-matter: Prepend front matter
to FILE. The TITLE, KEYWORDS, DATE, ID, SIGNATURE, and
FILE-TYPE are passed from the renaming command and are used to
construct a new front matter block if appropriate.
MODIFIED Function denote-rewrite-front-matter: Rewrites front
matter of note after denote-rename-file (or related). The FILE,
TITLE, KEYWORDS, SIGNATURE, DATE, IDENTIFIER, and
FILE-TYPE arguments are given by the renaming command and are used
to construct new front matter values if appropriate. If
denote-rename-confirmations contains rewrite-front-matter,
prompt to confirm the rewriting of the front matter. Otherwise
produce a y-or-n-p prompt to that effect. Thanks to
Jean-Philippe Gagné Guay for the pull request 558:
https://github.com/protesilaos/denote/pull/558.
denote package anymoredenote-journal integrates nicely with M-x calendarThe calendar can now highlight days that have journal entry. It may
also be used as a date picker to view or write a journal entry for
that day.
Thanks to Alan Schmitt for reporting an issue with the calendar integration during development: https://github.com/protesilaos/denote-journal/issues/8.
Thanks to Vineet C. Kulkarni for tweaking the identification of the journal keyword to be more robust: https://github.com/protesilaos/denote-journal/pull/4.
Thanks to Honza Pokorny for fixing two small issues with the path expansion:
Other than that, the package is providing the same functionality as
the discontinued denote-journal-extras.el.
denote-org is almost the same as the discontinued denote-org-extras.elThe only addition to dynamic blocks the optional :not-regexp parameter.
This is a regular expression that can further filter the results of a
search, such that the matching items are removed from the output.
The official manual of denote-org covers the technicalities.
Also thanks to Elias Storms for fixing a small issue with the “missing links” Org dynamic block, in pull request 486: https://github.com/protesilaos/denote/pull/486
denote-silo is the same as the discontinued denote-silo-extras.elI have only made small tweaks to it, but nothing that changes the user experience.
denote-markdown for some Markdown-specific extrasThis package provides some convenience functions to better integrate Markdown with Denote. This is mostly about converting links from one type to another so that they can work in different applications (because Markdown does not have a standardised way to define custom link types). It also defines an “Obsidian” file type which does not have any front matter but only uses a level 1 heading for the title of the note.
The code of denote-markdown used to be bundled up with the denote
package before version 4.0.0 of the latter and was available in the
file denote-md-extras.el. Users of the old code will need to adapt
their setup to use the denote-markdown package. This can be done by
replacing all instances of denote-md-extras with denote-markdown
across their configuration.
denote-sequenceUsers who want their notes to have an inherent structure can use
denote-sequence. The idea is to have thoughts that naturally form
sequences and are named accordingly. The sequence scheme is either
numeric or alphanumeric. The manual of the package explains all the
details.
I had a lot of fun developing this comprehensive package during the winter holidays.
Thanks to Claudio Migliorelli, Kierin Bell, Mirko Hernandez for helping me fix some issues during development:
consult-denote also gets a small updateThis has always been a standalone package. I made the function
consult-denote-file-prompt read the special-purpose variable
denote-file-prompt-use-files-matching-regexp. This is related to
commit e0f1d47 in denote.git, about issue 536 as reported by Alan
Schmitt: https://github.com/protesilaos/denote/issues/536. The
variable denote-file-prompt-use-files-matching-regexp is meant to be
let bound and is for advanced users or developers.
I will not develop new features or accept pull request for a couple of weeks. The idea is to focus on fixing any bug reports. We can then publish point releases quickly.
New features can be included after we are confident that the packages we have are okay.
This is just an overview of the Git commits, though remember that there is more that goes into a project, such as the reporting of inconsistencies, discussion of new ideas, et cetera. Thanks to everybody involved! Plus, some commits are large while others are tiny.
~/Git/Projects/denote $ git shortlog 3.1.0..4.0.0 --summary --numbered
470 Protesilaos Stavrou
90 Jean-Philippe Gagné Guay
6 Kierin Bell
4 Alan Schmitt
3 eum3l
2 Claudio Migliorelli
2 Lucas Quintana
2 grtcdr
1 Elias Storms
1 Laurent Gatto
1 Maikol Solís
1 Octavian
1 TomoeMami
The following are not accurate because they only reflect the changes after the reorganisation I made. But we have to start from somewhere.
~/Git/Projects/denote-journal $ git shortlog --summary --numbered
54 Protesilaos Stavrou
2 Honza Pokorny
1 Vineet C. Kulkarni
~/Git/Projects/denote-sequence $ git shortlog --summary --numbered
22 Protesilaos Stavrou
~/Git/Projects/denote-silo $ git shortlog --summary --numbered
17 Protesilaos Stavrou
~/Git/Projects/denote-org $ git shortlog --summary --numbered
15 Protesilaos Stavrou
~/Git/Projects/denote-markdown $ git shortlog --summary --numbered
11 Protesilaos Stavrou
I provide a big picture view of the Emacs Lisp programming language by combining prose with code. The goal is to give readers an idea of how Elisp works by showing some of the main concepts or patterns discernible in everyday code.
Some chapters are beginner-friendly, while others dive into deeper waters. Though I think everything is still approachable, as I try to explain basic concepts and take things one step at a time.
The book is not meant to be a replacement for the built-in Emacs Lisp Reference Manual. It simply gives you enough information to reason about Elisp. Once you start extending Emacs, the rest will follow naturally.
I hope you enjoy it and continue to have fun with Emacs.
]]>dired-previewBelow are the release notes
This version contains a few bug fixes and minor refinements that should improve the behaviour of the package.
In the past, dired-preview-mode could delete windows that held
another buffer, thus undoing the window layout that was present before
a preview buffer was displayed. Now dired-preview-mode makes sure to
only delete windows that have not had another buffer shown in them,
i.e. windows that were created just for preview purposes.
We tweaked how we test the type of the buffer-to-be-preview such that directories are not mistaken for “large files”. This was an issue for Mac computers that Sean Devlin brought to my attention in issue 27: https://github.com/protesilaos/dired-preview/issues/27.
dired-preview-delay has a 0.1 second minimum to avoid instabilityA value of 0 could lead to a noticeably degraded experience while navigating the Dired buffer. Thanks to Yiyu Zhou for reporting the matter in issue 2 on the GitLab mirror: https://gitlab.com/protesilaos/dired-preview/-/issues/2.
The user option dired-preview-ignored-extensions-regexp will now
also match files without an extension, such as the .DS_Store on Mac
computers.
Thanks to Sean Devlin for the contribution in pull request 26: https://github.com/protesilaos/dired-preview/pull/26. The change is within the ~15-line limit, meaning that Sean does not need to assign copyright to the Free Software Foundation.
The commands which scroll the other window, such as
scroll-other-window now operate on the preview buffer when that is
displayed. Thanks to Karthik Chikmagalur for proposing this in issue
24: https://github.com/protesilaos/dired-preview/issues/24.
This is in addition to the commands we already provided for scrolling
the preview window, namely, dired-preview-page-up and dired-preview-page-down.
Do M-x describe-keymap and then search for dired-preview-mode-map:
it is in effect when dired-preview-mode is enabled.
modus-themes package, I am introducing small changes to the modus-operandi-tinted and modus-vivendi-tinted themes. These concern fine details, some of which most users will probably not even notice. Though the cumulative effect of these changes is obvious once we compare the themes to their main counterparts, namely, modus-operandi and modus-vivendi. I am doing this to improve the consistency of the “tinted” themes. I think existing users will appreciate the attention to detail.
Below are some screen shots with the out-of-the-box design of the themes (remember that they are highly customisable). I also include pictures with spacious-padding-mode enabled (from my spacious-padding package). Notice that in this case the mode lines are just an overline, which is done by customising the user option spacious-padding-subtle-mode-line.
I plan to install those changes to emacs.git and make them available via GNU ELPA as part of modus-themes version 4.7.0, which I hope to publish some time this month or in May.
Highly accessible themes, conforming with the highest standard for colour contrast between background and foreground values (WCAG AAA). They also are optimised for users with red-green or blue-yellow colour deficiency.
The themes are very customisable and provide support for a wide range of packages. Their manual is detailed so that new users can get started, while it also provides custom code for all sorts of more advanced customisations.
Since August 2020, the original Modus themes (modus-operandi,
modus-vivendi) are built into Emacs version 28 or higher. Emacs 28
ships with modus-themes version 1.6.0. Emacs 29 includes version
3.0.0. Emacs 30 provides version 4.4.0. Version 4 is a major
refactoring of how the themes are implemented and customized. Such
major versions are not backward-compatible due to the limited
resources at my disposal to support multiple versions of Emacs and of
the themes across the years.
modus-themesdenote-journal package to
interact with the M-x calendar as part of their journaling workflow.
The new minor mode denote-journal-calendar-mode highlights dates in
the M-x calendar which have a corresponding Denote journal entry.
The applied face is called denote-journal-calendar: I made it draw
only a box around the date, thus respecting existing colouration. Here
is a demonstration, which also includes red-coloured dates for holidays:
The denote-journal-calendar-mode is buffer-local and meant to be
activated inside the M-x calendar buffer, thus:
(add-hook 'calendar-mode-hook #'denote-journal-calendar-mode)
While navigating the calendar buffer, use the command
denote-journal-calendar-find-file to visit the Denote journal entry
corresponding to the date at point. If there are multiple journal
entries, the command will prompt you to select one among them.
The command denote-journal-calendar-new-or-existing creates a new
journal entry for the date at point or visits any existing one. This is
like denote-journal-new-or-existing-entry but for the given M-x
calendar date.
Remember that I have split denote into several packages, one of
which is denote-journal. I plan to coordinate the release of new
versions across all Denote-related packages, so expect the
aforementioned to be available at around the same time as denote
version 4.0.0 (which is going to be massive, by the way).
The denote-journal package makes it easier to use Denote for
journaling. While it is possible to use the generic denote command
(and related) to maintain a journal, this package defines extra
functionality to streamline the journaling workflow.
The code of denote-journal used to be bundled up with the denote
package before version 4.0.0 of the latter and was available in the
file denote-journal-extras.el. Users of the old code will need to
adapt their setup to use the denote-journal package. This can be
done by replacing all instances of denote-journal-extras with
denote-journal across their configuration.
denote-journalDenote is a simple note-taking tool for Emacs. It is based on the idea that notes should follow a predictable and descriptive file-naming scheme. The file name must offer a clear indication of what the note is about, without reference to any other metadata. Denote basically streamlines the creation of such files while providing facilities to link between them.
Denote’s file-naming scheme is not limited to “notes”. It can be used for all types of file, including those that are not editable in Emacs, such as videos. Naming files in a consistent way makes their filtering and retrieval considerably easier. Denote provides relevant facilities to rename files, regardless of file type.
denoteIn this ~16-minute video, I demonstrate the new, in-development “query links” functionality of Denote. These are links that trigger a search when you interact with them. There are two types of query links: (i) search in file contents, or (ii) search in file names. When there are matches for a given query, those are displayed in a separate buffer, which uses the appropriate major mode. Query links complement the “direct links” Denote has always supported. Internally, they use the same infrastructure that Denote backlinks rely on (and we have had backlink support since the beginning).
Denote is a simple note-taking tool for Emacs. It is based on the idea that notes should follow a predictable and descriptive file-naming scheme. The file name must offer a clear indication of what the note is about, without reference to any other metadata. Denote basically streamlines the creation of such files while providing facilities to link between them.
Denote’s file-naming scheme is not limited to “notes”. It can be used for all types of file, including those that are not editable in Emacs, such as videos. Naming files in a consistent way makes their filtering and retrieval considerably easier. Denote provides relevant facilities to rename files, regardless of file type.
denotedenote: link type infrastructure but exhibit a
different behaviour than the direct links we have always had. Instead
of pointing to a file via its unique identifier, they initiate a
search through the contents of all files in the denote-directory.
This search uses the built-in Xref mechanism and is the same as what
we have already been doing with backlinks (basically, a grep).
In short:
denote:20250324T074132 resolves to a file path.
Clicking on the link opens the corresponding file. Org export will
also take care to turn this into a file path.denote:this is a test
produces a buffer listing all matches for the given query. Clicking
on the matching line in that buffer opens the file at that point
(just how our backlinks work when they show context—I am
generalising this mechanism).Direct links can point to any file, including PDFs, videos, and pictures (assuming it is renamed to use the Denote file-naming scheme). Whereas query links are limited to text files.
This is a work-in-progress that lives on its own branch as of this writing. I will not elaborate at length right now as the implementation details may change. I have, nonetheless, created an issue on the GitHub repository where interested parties can provide their feedback. It also includes some screenshots I took: https://github.com/protesilaos/denote/issues/561. The code includes other changes which pertain to how we handle backlinks and constitutes a simplification of the code base.
The idea is to add the functionality to the main branch in the
coming days or weeks. Then I will do a video about it and/or explain
more.
That granted, do not forget that the official manual is the most up-to-date reference and the single source of truth.
Denote is a simple note-taking tool for Emacs. It is based on the idea that notes should follow a predictable and descriptive file-naming scheme. The file name must offer a clear indication of what the note is about, without reference to any other metadata. Denote basically streamlines the creation of such files while providing facilities to link between them.
Denote’s file-naming scheme is not limited to “notes”. It can be used for all types of file, including those that are not editable in Emacs, such as videos. Naming files in a consistent way makes their filtering and retrieval considerably easier. Denote provides relevant facilities to rename files, regardless of file type.
[ Further down on this list I include more of my Denote-related packages. ]
denotemaster branch of the emacs.git repository
for many years now. It helps me test new features and make necessary
adjustments to all the packages I develop/maintain. Below I explain
how I make this happen on my computer, which is running Debian stable
(Debian 12 “Bookworm” as of this writing). If you are a regular user,
there is no reason to build from source: just use the latest stable
release and you should be fine.
apt development sourcesTo build Emacs from source on Debian, you first need to have the
deb-src package archive enabled. In your /etc/apt/sources.list
file you must have something like this:
deb http://deb.debian.org/debian/ bookworm main
deb-src http://deb.debian.org/debian/ bookworm main
After modifying the sources, run the following on the command line to fetch the index with new package names+versions:
sudo apt update
Now that you have enabled the deb-src archive, you can install the
build dependencies of the Debian emacs package with the following on
the command line:
sudo apt build-dep emacs
With this done, you are ready to build Emacs from source.
You need the git program to get the source code from the emacs.git
website. So install it with this command:
sudo apt install git
Now make a copy of the Emacs source code, using this on the command line:
git clone https://git.savannah.gnu.org/git/emacs.git ~/path/to/my/copy-of-emacs.git
Replace ~/path/to/my/copy-of-emacs.git with the actual destination
of your preference. I have a ~/Builds directory where I store all
the projects I build from source. I thus do:
git clone https://git.savannah.gnu.org/git/emacs.git ~/Builds/emacs.git
If the cloning process is too slow on your end, perform a shallow clone instead. For example:
git clone --depth 1 https://git.savannah.gnu.org/git/emacs.git ~/Builds/emacs.git
And if the Savannah website is not responsive, then clone from the
GitHub mirror (with the --depth 1 if necessary):
git clone https://github.com/emacs-mirror/emacs.git ~/Builds/emacs.git
Assuming you have the copy of emacs.git stored at ~/Builds/emacs.git,
you switch to that directory with the following:
cd ~/Builds/emacs.git
Keep in mind that unless you explicitly switch to another branch, you
are on master, i.e. the latest development target.
NOTE: All subsequent commands are ran from your equivalent of
~/Builds/emacs.git.
autogen.sh the first timeThis script will generate the configuration scaffold. You only really need to do this once (and I always forget about it for this very reason). Simply do this on the command line:
./autogen.sh
It checks that you have all you need to get started and prints output like this:
Checking whether you have the necessary tools...
(Read INSTALL.REPO for more details on building Emacs)
Checking for autoconf (need at least version 2.65) ... ok
Your system has the required tools.
Building aclocal.m4 ...
Running 'autoreconf -fi -I m4' ...
Building 'aclocal.m4' in exec ...
Running 'autoreconf -fi' in exec ...
Configuring local git repository...
'.git/config' -> '.git/config.~1~'
git config transfer.fsckObjects 'true'
git config diff.cpp.xfuncname '!^[ ]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])
^((::[[:space:]]*)?[A-Za-z_][A-Za-z_0-9]*[[:space:]]*\(.*)$
^((#define[[:space:]]|DEFUN).*)$'
git config diff.elisp.xfuncname '^\([^[:space:]]*def[^[:space:]]+[[:space:]]+([^()[:space:]]+)'
git config diff.m4.xfuncname '^((m4_)?define|A._DEFUN(_ONCE)?)\([^),]*'
git config diff.make.xfuncname '^([$.[:alnum:]_].*:|[[:alnum:]_]+[[:space:]]*([*:+]?[:?]?|!?)=|define .*)'
git config diff.shell.xfuncname '^([[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*\(\)|[[:alpha:]_][[:alnum:]_]*=)'
git config diff.texinfo.xfuncname '^@node[[:space:]]+([^,[:space:]][^,]+)'
Installing git hooks...
'build-aux/git-hooks/commit-msg' -> '.git/hooks/commit-msg'
'build-aux/git-hooks/pre-commit' -> '.git/hooks/pre-commit'
'build-aux/git-hooks/prepare-commit-msg' -> '.git/hooks/prepare-commit-msg'
'build-aux/git-hooks/post-commit' -> '.git/hooks/post-commit'
'build-aux/git-hooks/pre-push' -> '.git/hooks/pre-push'
'build-aux/git-hooks/commit-msg-files.awk' -> '.git/hooks/commit-msg-files.awk'
'.git/hooks/applypatch-msg.sample' -> '.git/hooks/applypatch-msg'
'.git/hooks/pre-applypatch.sample' -> '.git/hooks/pre-applypatch'
You can now run './configure'.
Do not be intimidated by it. Focus on the final line instead, which
directs you to the configure directive.
How exactly you build Emacs depends on your preferences and system-specific requirements. At the end of this post, I copy my current configuration, though I advise against copying it without understanding what it does.
If you have no specific preferences, just use the defaults by running this on the command line:
./configure
It will set up the build environment for you. If, however, you wish
to explore your options and customise the emacs program you will
get, then issue the following command and carefully read its output:
./configure --help
The minimum I recommend is to specify where the build artefacts are stored. I use this, which has not caused me any issues over the years:
./configure --prefix=/usr/local
Once you have understood the available options, go ahead and run
configure. For example:
./configure --prefix=/usr/local --with-x-toolkit=gtk3
Whenever you need to rebuild Emacs with some new flags, run the
configure command again, passing it the relevant flags. If you wish
to keep the same options for a new build, then simply do not run
configure again.
Once configure finishes its work, it is time to run the make
program. For new builds, this is as simple as:
make
Sometimes you have old build artefacts that conflict with changes upstream. When that happens, the build process will fail. You may then need to use:
make bootstrap
In general, make is enough. It will be slow the first time, but will
be faster on subsequent runs as it reuses what is already there. A
make bootstrap will always be slow though, as it generates
everything anew.
After make is done, you are ready to install Emacs:
sudo make install
You will not need escalated privileges (i.e. sudo) is you specified
a --prefix with a user directory during the configure step. How
you go about it is up to you.
Whenever you wish to update from source, go to where your copy of
emacs.git is (e.g. ~/Builds/emacs.git) and pull the latest changes
using the git program:
git pull
Then repeat make and make install. Remember that you do not need
to re-run configure unless you specifically want to modify your
build (and if you do that, you probably need to make bootstrap).
NEWSEmacs users can at all times learn about changes introduced in their
current version of Emacs with M-x view-emacs-news. It is bound to
the key C-h n by default. This command opens the current NEWS
file. With a numeric prefix argument, you get the NEWS of the given
Emacs version. For example, C-u 27 C-h n shows you what Emacs
version 27 introduced.
NEWS to those of emacs.gitWith the help of the built-in Emacs ediff package, you can compare
your latest NEWS to those coming from emacs.git. I always do this
after pulling the latest changes from source (with git pull).
From the root directory of your copye of emacs.git (e.g.
~/Builds/emacs.git), and while using Emacs, you can do M-x
project-find-file (C-x p f) to search the Emacs “project” for a
file called etc/NEWS. This is where the latest user-facing changes
are recorded.
If you are not sure where you are on the filesystem while inside
Emacs, do M-x cd (or M-x dired or M-x find-file), select the
root directory of your emacs.git, hit RET, and then do M-x
project-find-file.
Now that you have emacs.git/etc/NEWS in a buffer, also load your
copy of NEWS with M-x view-emacs-news (C-h n).
Then do M-x ediff-buffers, which will prompt for two buffers to
compare. First select your version of NEWS and then that of emacs.git.
NOTE: I think the default Ediff interface is problematic. Put the following in your configuration to make it work in a single frame:
(setq ediff-split-window-function 'split-window-horizontally)
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
Also watch my video with the Ediff basics: https://protesilaos.com/codelog/2023-11-17-emacs-ediff-basics/.
This is it. You are now in the flow of building Emacs from source. Good luck with everything!
--prefix=/usr/local --without-xinput2 --without-compress-install --without-gpm --without-selinux --with-native-compilation=yes --with-sound=no --without-gif --without-tiff --with-cairo --with-harfbuzz --with-tree-sitter=ifavailable --with-json --without-gsettings --without-gconf --with-x-toolkit=no --without-toolkit-scroll-bars --without-xft --without-xaw3d
I am not updating old publications, unless otherwise noted. The most up-to-date recode of my Emacs build is documented in my dotemacs: https://protesilaos.com/emacs/dotemacs.
Inspect the value of the Emacs variable system-configuration-options
to find out how your Emacs is built.
Have fun!
]]>Though it turns out we can do a lot of useful things on top of this simple-yet-powerful idea: custom Dired listings, Org dynamic blocks, sequence notes, journaling, and many more. Having a clear separation between core and extensions makes it easier for us to implement all the features we want without worrying that the main package is becoming bloated.
Concretely, much of the functionality that was part of the denote
package will now be provided by other packages. To this end, I just
made a change to the official elpa.git repository:
commit f23ff49fb8df22390fe139b432f763860a354175
Author: Protesilaos Stavrou <[email protected]>
Date: Fri Mar 14 07:14:55 2025 +0200
* elpa-packages (denote-journal, denote-markdown, denote-org, denote-sequence, denote-silo): Add new packages
elpa-packages | 10 ++++++++++
1 file changed, 10 insertions(+)
What remains to be done is for me to merge the reorganise-denote
branch of denote.git into main. I will do this as soon as the new
packages are indexed on GNU ELPA (maybe later today or tomorrow).
Users of the GNU ELPA package will not be affected immediately. Those who build from source will, however, have to take action.
Users will experience breaking changes when they update to the new
denote package: functionality will be missing. In principle, all
those should be easy to fix: install the appropriate new package and
rename the functions/variables in your configuration accordingly. In
detail:
denote-journal-extras.el becomes the package
denote-journal. In your configuration replace
denote-journal-extras with denote-journal.denote-org-extras.el (Org dynamic blocks and related)
becomes the package denote-org. In your configuration replace
denote-org-extras with denote-org.denote-silo-extras.el becomes the package denote-silo.
In your configuration replace denote-silo-extras with
denote-silo.Additionally, the files that were in development but never made it as
part of a formal release, namely, denote-md-extras.el and
denote-sequence.el, will debut as their own packages:
denote-markdown and denote-sequence, respectively.
I am reluctant to introduce breaking changes, though this has to be done for the long-term wellness of the project. If you have any questions, please contact me directly or open an issue on the GitHub repository. I am happy to help you.
All new packages are marked as version 0.0.0, which means that only
those who track GNU-devel ELPA (or build from source) will notice
them. The formal release will coincide with the new version of
denote (4.0.0), which I expect to publish some time in April or
May 2025.
On Saturday, the 15th of March 2025, at 14:00 o’clock of time zone Europe/Athens, I will do a live stream where I will answer every question posted in the chat. The idea is to cover any of the topics I already write/talk about on my website, including Emacs, free software, politics, philosophy, and everyday affairs.
I will answer every question from top to bottom to the best of my abilities and will not give any of those formulaic non-answers.
The plan is to do a minimum of two hours, but may extend it for at least one or two more hours depending on the participation in the chat. My electricity should be okay because the weather forecast shows a clear day with fairly warm temperatures (finally!).
The event will be recorded, so no worries if you cannot make it live.
Talk to all of you soon!
]]>regular-editing preset
and another for presentation-mode (these are arbitrary, user-defined
symbols): the former uses small fonts which are optimised for writing,
while the latter applies typefaces that are pleasant to read at
comfortable point sizes.
fontaineBelow are the release notes.
This version changes the underlying implementation of Fontaine’s font configuration presets. In principle, this should not have any effect on how users experience the package, though there are some important details that are different.
Fontaine has always modified typography-related faces, such as
default, fixed-pitch, and variable-pitch, to apply the font
family, height, and weight specified by the user. In the past, this
was done in a way that could get overridden under certain conditions,
such as by loading a theme after setting a Fontaine preset configuration.
By making Fontaine a theme, we guarantee that its settings are not
undone. In practice, this means that users do not have to re-apply the
current preset after loading a theme. The function fontaine-apply-current-preset
is thus obsolete.
In Emacs, a “theme” is a bundle of configurations. Those typically
cover colours (such as with my modus-themes), but a theme can focus
on other settings as well. For example, the popular use-package is
internally done as a theme (check the value of custom-known-themes).
Fontaine is a theme in the same way use-package is, meaning that it
will (i) persist its effects, (ii) not show up in the
custom-enabled-themes and so not be affected by something like
(mapc #'disable-theme custom-enabled-themes),
and (iii) not be an option among those presented by load-theme.
There are no known bugs, though please contact me if you encounter a scenario where Fontaine does not do the right thing. Thanks, in this regard, to Haruko and Emily Hyland for reporting a couple of bugs:
In the past, users could apply a Fontaine preset to the current frame without affecting other frames. While this could be useful in certain situations, it was ultimately making the code more complex for marginal gains. As part of the transition to a theme, which is anyway global, I am removing everything related to frame-specific functionality.
The fontaine-generic-face-families are used when necessary to
guard against nil values. Those font families are symbolic
references to whatever the operating system is configured to use
(e.g. on Linux this is handled by fontconfig).
If Fontaine is instructed to load an invalid preset, it displays a warning and does nothing else. Before, it would produce an error, which could prevent Emacs from starting up normally if this were to happen at startup. A warning is enough to inform the user of what is happening.
Same principle as above when Emacs is not ran in a graphical interface. In text terminals, Fontaine cannot work because it is not possible to have different font families, styles, and heights, than those of the terminal (hence the backronym of FONTAINE “Fonts, Ornaments, and Neat Typography Are Irrelevant in Non-graphical Emacs”). Thanks to Jorge Gomez for the patch in pull request 13: https://github.com/protesilaos/fontaine/pull/13. Further tweaks by me.
The fontaine-toggle-preset command will produce an error if it
cannot find the preset it is supposed to switch to. The toggle is
between the last two loaded presets, as done by the command
fontaine-set-preset (the fontaine-mode takes care to persist the
relevant history).
dired-previewBelow are the release notes
This version contains several refinements and bug fixes.
Preview buffers have a prefix to their name to make them stand out.
This is controlled by the user option dired-preview-buffer-name-indicator,
which is a string that defaults to [P].
The way dired-preview works is to display a buffer and then keep a
list of preview buffers to economise on redisplaying it again. This
list of buffers is relevant for as long as we are in the Dired buffer,
otherwise all buffers therein are killed (buffers that were alive
before being previewed are not touched).
By default we delete from oldest to newest the accumulated buffers
when they exceed 10 in total. Though users can modify this behaviour
by editing the value of the new user option dired-preview-kill-buffers-method
(its doc string explains the technicalities).
Thanks to artelse for discussing this with me in issue 20: https://github.com/protesilaos/dired-preview/issues/20.
dired-preview-display-action-alist has a new optional functionThe dired-preview-display-action-alist is the user option which
controls where the preview window is displayed. Its value can either
be the symbol of a function or a display-buffer-alist entry.
By default, we have a “do-what-I-mean” function that tries to find a
good placement for the window. The new dired-preview-display-action-alist-below
function has a straightforward behaviour: it always shows the preview
below the current window and it always makes the preview window 0.3
times the height of the Emacs frame.
This is to ensure that potentially sensitive contents are not displayed by accident, such as during a video call.
We should not trigger a preview when the cursor is over the implicit
. directory, as that causes a recursion that breaks things. Thanks
to Inkbottle007 for reporting the bug in issue 23:
https://github.com/protesilaos/dired-preview/issues/23.
Fixed a scenario where we would try to delete the last available window on the current frame. This should never happen. Thanks to artelse for reporting a relevant bug in the discussion of issue 22: https://github.com/protesilaos/dired-preview/issues/22.
Fixed a case when hexl-follow-ascii could fail to find an overlay
under certain conditions. This did not create any noticeable
problems, though having an error there would interfere with any
workflow that would rely on toggle-debug-on-error.
The preview window will automatically be closed if the user switches
outside the given Dired buffer. We now do not consider a change to
the minibuffer as being “outside” this context. This way, a quick
M-x to, say, enable a minor mode does not have any effect on the
window layout.
Suppressed the messaging facility of the underlying tracking of preview buffers. Otherwise, Dired would notify us that the directory has changed whenever we would preview a new one, which is superfluous.
The body of the dired-preview-trigger function, which determines
whether a preview will be displayed, is encapsulated in a condition-case.
This helps capture errors and thus have a more predictable behaviour.
The dired-preview-display-action-alist has a more accurate
declaration which allows for its correct customisation inside the
Custom UI interface. In particular, it will behave the same way as
the display-buffer-alist, where relevant.
Below are the release notes.
This release includes two stylistic corrections that pertain to Aporetic Serif and Aporetic Serif Mono.
The first change is to the slanted (italic) form of the letter t.
Before, it was mistakenly set to have a curved, upward-facing bottom
stroke, which would clash with the flat bottom of i and l. Now
the slanted t has a flat bottom as intended. The upright (roman)
variants are always flat in this regard.
The second change is to the letter m in both upright and slanted
forms. Before, the m would have a top left serif, as intended, but
not a bottom right tail. The tail is a feature of other glyphs that
need to have such a style, like a, h, n, u: it imposes a
proper rhythm together with the rest of the serif details. Now the m
has its missing bottom right tail, making everything consistent.
[ The m has a shorter middle leg in all the “mono” families” to
improve readability, especially at small point sizes. The
proportionately spaced fonts use a normal middle leg, as m is
naturally wider there and thus is already perfectly legible. The
other details are the same. ]
denote package to have a clear separation
between “core” and “extensions”. The idea is to decouple the two. The
denote package shall provide only the core functionality, while all
other features we already have will be available as standalone
packages.
The reason I am doing this is because the project has organically grown over time to encompass lots of useful-yet-inessential applications, such as Org dynamic blocks, journaling capabilities, and sequence schemes, among others.
All those extras are nice to have, though they dilute the message
about what Denote is, making it seem far more complex than it actually
is. They are also held back by the minimalist outlook of the core:
they cannot be developed to their logical end, as any dependency they
incorporate becomes a dependency of the whole project, which makes no
sense (e.g. we can have a transient.el to interact with Denote
commands, but this is in no way essential, so why force it upon
everyone who downloads the denote package?).
Denote essentially is a file-naming scheme. We create new files or rename existing ones to have file names that are easy to retrieve with even basic tools. This is my use-case and the reason I wrote Denote: I name my videos, PDFs, pictures, and “notes” with the Denote file-naming scheme, making it easy for me to find everything.
I think the Denote file-naming scheme is ingenious, though the real value is in having a scheme—any scheme—to force consistency in how you name things. Consistency begets predictability, which in turn increases the likelihood of finding your data.
The other part of retrieving information is through links. Part of the
Denote file-naming scheme is the date+time, which is a unique
identifier. We can thus link to any file in the denote-directory
using its identifier. Once we have these “forward links”, we can
easily figure out what the “backward links” of a given file are, i.e.
which files link to the current one.
This is the core, plus a few other conveniences that I need not enumerate herein.
Anything that builds on the aforementioned is an “extension” and will
have its own Git repository as well as user manual. To this end, I
have already removed denote-sequence.el from the denote core and
made it its own entity:
denote-sequence (⚠️ Not available yet)The plan is to do the same with the “Org extras”, such as with all the Org dynamic blocks, the “Markdown extras”, the “journal extras”, and the “silo extras”. Once all the packages are ready for widespread use, I will add them to GNU ELPA. Until then, everything is a WORK-IN-PROGRESS.
For the time being, the technical discussion is done in issue 543:
https://github.com/protesilaos/denote/issues/543. The code which
will eventually be merged into the main branch resides in the
reorganise-denote branch: https://github.com/protesilaos/denote/tree/reorganise-denote.
There will be no reduction in the total set of features we provide. This is only a matter of reorganising what we have, namely, to (i) make it easier for new users to understand what Denote is, (ii) pick only the extensions they require, (iii) make it possible to decentralise the maintenance of the project should I ever need to step down (which is not happening, but as a matter of principle).
Those granted, keep in mind that Denote is not a “second brain” and will not make you smarter. It is a flexible and capable tool, truly Emacs-y in its adaptability, that you can use as part of a workflow that makes sense to you. Let us then decouple the core from its extensions and continue to give users the best possible experience with every piece of code and documentation that we write.
]]>Aporetic fonts:
Iosevka Comfy:
“Iosevka” is a reserved name. I had not realised this until I reread the SIL Open Font License, Version 1.1, specifically:
3 No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
I wanted to change the names of the font families to make them more intuitive. Instead of something like “Iosevka Comfy Motion Duo” I now have “Aporetic Serif”. The full list is further below.
I also had to reduce the number of variants and font weights to both (i) streamline what I am offering and (ii) make it possible to build the fonts on my computer.
The fonts are these:
| Current name | Old name |
|---|---|
| Aporetic Sans | Iosevka Comfy Duo |
| Aporetic Serif | Iosevka Comfy Motion Duo |
| Aporetic Sans Mono | Iosevka Comfy |
| Aporetic Serif Mono | Iosevka Comfy Motion |
I no longer provide “fixed” and “wide” variants.
All glyphs are the same as before except for the zero (0), which is
now always rendered with a forward slash and a wider body. The reason
for this change is that the narrow oval shape could be mistaken for a
Greek theta (θ).
Font weights are also reduced to just regular and bold.
It is a nice Greek word and no other fonts with this name exist.
]]>(use-package altcaps
:ensure t
:bind
("C-x C-a" . altcaps-dwim)
:config
;; Optionally force letter casing for certain characters (for legibility).
(setq altcaps-force-character-casing
'(("i" . downcase)
("l" . upcase))))
altcapsThe altcaps package is a small, focused-in-scope tool that helps
users communicate mockery or sarcasm effectively. It does this by
alternating the letter casing of characters in the words it affects.
altcapsIn this 25-minute video I show two ways to organise your Emacs init.el file. One approach is to keep editing the code in Emacs Lisp and to leverage the “outline”, while the other is to use a literate Org file whose code blocks will be “tangled” to the init.el. I explain all the technicalities and show examples. More resources:
Sources:
show-fontBelow are the release notes.
This version includes quality-of-life refinements.
The command show-font-tabulated will produce a listing of font
families and their preview that uses the built-in tabulated interface.
This interface is the same as the one used by M-x list-packages.
Users can sort fonts by font family name (move point to the given
column and type S or call M-x tabulated-list-sort).
When show-font-tabulated is called with a prefix argument (C-u by
default), it prompts for a string or regular expression. It then shows
only the font families matching the given input.
The command show-font-list which we already had in version 0.1.0 is
like the show-font-tabulated, but uses a bespoke buffer where each
font and its preview are shown one after the other. Now it also
accepts an optional prefix argument to limit the list to only the
matching fonts.
show-font-display-buffer-action-alist controls buffer placementThe preview buffers we use will now conform with the value of the new
user option show-font-display-buffer-action-alist. This is a more
advanced feature, due to how display-buffer works, so you may want
to check the video I did recently about controlling where buffers are
displayed: https://protesilaos.com/codelog/2024-02-08-emacs-window-rules-display-buffer-alist/.
The default value of show-font-display-buffer-action-alist will show
the buffer at the bottom of the frame.
beframe enables a frame-oriented Emacs workflow where each frame has
access only to the list of buffers visited therein. In the interest of
brevity, we call buffers that belong to frames “beframed”.
beframeBelow are the release notes
This version contains quality-of-life refinements to an already stable package.
We provide the user option beframe-rename-function, which will be
called with the new frame when beframe-mode is enabled. The idea is
to automatically apply a helpful name to the frame that was created.
The default function we use is beframe-rename-frame, which will do
the right thing to get a suitable name. To make this even more robust,
we now disambiguate equal frame names by appending a number to their
name. So instead of having two or more frames all named hello, you
get hello, hello<2>, and so on.
Thanks to Vedang Manerikar for the original contribution in pull request 12: https://github.com/protesilaos/beframe/pull/12. The change is within the ~15-line limit, meaning that Vedang does not need to assign copyright to the Free Software Foundation (though I believe the paperwork is done anyway). I made some further changes on top.
Remember that you can make certain commands automatically generate a
frame and run therein by adding them to the list of beframe-functions-in-frames.
A common use-case is to do this for switching to a new project, hence:
(setq beframe-functions-in-frames '(project-prompt-project-dir))
beframe-transient instead of the prefix key mapWe provide a regular prefix key map where Beframe commands are bound to. Users can access all the commands via a prefix key, such as with:
(define-key global-map (kbd "C-c b") #'beframe-prefix-map)
Users who prefer a more graphical interface can instead rely on the
new beframe-transient. It is the same principle as the prefix key
map:
(define-key global-map (kbd "C-c b") #'beframe-transient)
[ The difference between the two interfaces is small when using the
which-key package. ]
While using the beframe-mode, the standard read-buffer-function is
set to a Beframe function that prompts for a buffer. The idea is to
filter the list of buffers to only show those that are specific to the
current/given frame. To make this more clear, the text of the prompt
now has [Beframed] prepended to it.
I do not think we need a user option for this, though I am happy to reconsider if there is a good reason for it.
Fixed the function aliases of the “assume” and “unassume” commands that take a regular expression as input to perform their operation. The old aliases where written in the wrong way, such that they were rendering the original function void.
Bound a few more commands to the beframe-prefix-map. Everything
should now be there, to improve discoverability (remember that C-h
after an incomplete key sequence will produce a Help buffer that
lists all the keys+commands which extend the given key sequence).
The name of the beframe-buffer-menu buffer is more descriptive.
The command beframe-buffer-menu puts the beframed list of buffers
in a buffer. Its old naming scheme was *Buffer list for NAME*,
where NAME is the name of the frame. Whereas now it is *Buffer
list for ‘NAME’ frame*.
Same as above for the frame-specific scratch buffers. Those are
generated for new frames when beframe-create-frame-scratch-buffer
is non-nil (the default) and beframe-mode is enabled.
In this ~45-minute video I cover the basics of managing your task list with Org mode. The idea is to write tasks in a simple file and then use the Org agenda views to display and filter the tasks you are interested in. Throughout the video I also comment on relevant points about the overall workflow. The basic configuration I show in the video is below:
;; These are the defaults we want to change. We do so in the
;; following `use-package' declaration.
(setq org-M-RET-may-split-line '((default . t)))
(setq org-insert-heading-respect-content nil)
(setq org-log-done nil)
(setq org-log-into-drawer nil)
(use-package org
:ensure nil ; do not try to install it as it is built-in
:config
(setq org-M-RET-may-split-line '((default . nil)))
(setq org-insert-heading-respect-content t)
(setq org-log-done 'time)
(setq org-log-into-drawer t)
(setq org-directory "/tmp/testing-org/")
(setq org-agenda-files (list org-directory))
;; Learn about the ! and more by reading the relevant section of the
;; Org manual. Evaluate: (info "(org) Tracking TODO state changes")
(setq org-todo-keywords
'((sequence "TODO(t)" "WAIT(w!)" "|" "CANCEL(c!)" "DONE(d!)"))))
1=1=2 refers to the second child of
the first child of the parent note 1.
A popular sequencing scheme is to combine numbers with letters
(Luhmann-style folgezettel). This alphanumeric expression is more
compact, though it might also be a bit harder to reason about. For
example, 1a2 is the same as 1=1=2, which looks clean, but it gets
tricky once you reach 1za5zx which is equivalent to 1=27=5=50.
Unlike the numeric scheme where there is an explicit depth delimiter
(the =), the alphanumeric scheme establishes depth by alternating
between numbers and letters. Thus, 1a2 and 1=1=2 are both three
levels of depth.
I am in the process of giving users the option. They will be able to pick their sequencing scheme and then produce notes, re-parent them, and so on, with Denote taking care to use/generate the correct sequence each time. In principle, the two schemes are interoperable and there will be relevant commands to switch between them.
[ UPDATE 2025-01-12 11:25 +0200: Now merged into main and
deleted the feature branch. ]
Below is the commit I worked on today. For now it lives in the alphanumeric-sequence-extension
branch: https://github.com/protesilaos/denote/tree/alphanumeric-sequence-extension.
I will merge it into main as soon as I am done with the user-facing
parts, which I will probably do tomorrow or early next week. If you
are interested to try out what is now available, please check the
source code and let me know your thoughts.
commit 43bd30e6ebd9e948a390d11bc3ec84cf80e74576
Author: Protesilaos Stavrou <[email protected]>
Date: Sat Jan 11 19:37:56 2025 +0200
Make the groundwork for alphanumeric (Luhmann) sequences in denote-sequence.el
The idea is to support both numeric and alphanumeric sequencing
schemes, as documented in the new user option 'denote-sequence-scheme'.
We now have the tools to correctly split, join, increment, and convert
input accordingly, such that we can, for example, accurately produce a
child of sequence "1a2" (we could already do that for numeric
sequences).
What is covered herein is just the groundwork. I still need to extend
the helper functions which directly support the creation of new
parent, child, or sibling sequences. While this still is a lot of
work, the hard part is now done.
denote-sequence.el | 338 +++++++++++++++++++++++++++++++++++++++------------
tests/denote-test.el | 34 +++++-
2 files changed, 291 insertions(+), 81 deletions(-)
Denote is a simple note-taking tool for Emacs. It is based on the idea that notes should follow a predictable and descriptive file-naming scheme. The file name must offer a clear indication of what the note is about, without reference to any other metadata. Denote basically streamlines the creation of such files while providing facilities to link between them.
Denote’s file-naming scheme is not limited to “notes”. It can be used for all types of file, including those that are not editable in Emacs, such as videos. Naming files in a consistent way makes their filtering and retrieval considerably easier. Denote provides relevant facilities to rename files, regardless of file type.
denoteOver at Oxford University they use their own calendaring scheme to label the terms and weeks of their school year. Why? Because they can! And why would some guy in the mountains of Cyprus care? Because it’s fun to write Emacs Lisp!
The academic year is divided into three periods of teaching and three
of vacation. The latter have no special names, while the former are
called “Michaelmas”, “Hilary”, and “Trinity”. Each teaching term
consists of a few weeks whose numbering starts from 1.
With some custom code, we can configure the Emacs M-x calendar to
tell us (i) which term we are in for the current month of the Oxford
academic year, if any, (ii) which term and week number we are in, if
any, and (iii) which is the standard week number.
Here is how it looks without any customisations. Serviceable, but not conducive to the Oxford culture.
It is busier, for sure, though this is what you get for being at Oxford. Notice that when there is nothing Oxford-related, we just show the regular calendar information.
UPDATE 2025-01-09 18:36 +0200: I revised a few lines of code to
(i) work with either Sunday or Monday as the first day of the week,
(ii) not show any Oxford week beyond the years specified in
prot-oxford-dates.
I wrote this over the course of a few hours. It may be refined here
and there, but I think it is already good enough. The only major
improvement would be to implement the formula that Oxford uses to
derive their dates. As I do not know it, the prot-oxford-dates have
to be updated manually each year.
;; NOTE 2025-01-09: Perhaps there is some formula to always get the
;; dates, but I am not aware of it. As such, these dates need to be
;; updated at the start of each school year.
;;
;; Source: <https://www.ox.ac.uk/about/facts-and-figures/dates-of-term>.
(defvar prot-oxford-dates
'((michaelmas (10 13 2024) (12 7 2024))
(hilary (1 19 2025) (3 15 2025))
(trinity (4 27 2025) (6 21 2025)))
"Alist of Oxford calendar terms with start and end date.
Each element is of the form (NAME START END), where NAME is the name of
the term, as a symbol, START is the start date and END is the end date.
START and END each are of the form (month day year), where each element
is a number.")
(defun prot-oxford--get-iso-week (date)
"Get the ISO week of DATE.
DATE is a list of the form (month day year)."
(unless (calendar-date-is-valid-p date)
(error "The date `%s' does not conform with `calendar-date-is-valid-p'" date))
(car
(calendar-iso-from-absolute
(calendar-absolute-from-gregorian date))))
(defun prot-oxford--get-term-week (term-start-week term-end-week iso-week prefix)
"Return the number of the Oxford term week or nil.
Determine the week given TERM-START-WEEK and TERM-END-WEEK as Gregorian
week numbers. Compare ISO-WEEK to them.
If `calendar-week-start-day' is a Monday, then start counting weeks from
0, because Oxford weeks start from Sunday (otherwise, Week 1 includes 6
days before the first Sunday).
When returning the number, concatenate it with PREFIX. PREFIX is a
single letter string. A longer PREFIX is trimmed accordingly."
(when (and term-start-week term-end-week iso-week)
(when-let* ((offset (pcase calendar-week-start-day
(0 1)
(1 0)))
(number (cond
((> iso-week term-end-week)
nil)
((= term-start-week iso-week)
offset)
((< term-start-week iso-week)
(+ (- iso-week term-start-week) offset))))
(pre (if (> (length prefix) 1)
(substring prefix 0 1)
prefix)))
(concat pre (number-to-string number)))))
(defun prot-oxford--get-term-weeks (term year)
"Return Oxford TERM start and end week numbers as a list.
Check YEAR to determine if the date is out of bonds of the
`prot-oxford-dates'."
(pcase-let* ((`(,beg-date ,end-date) (alist-get term prot-oxford-dates))
(`(,_ ,_ ,term-year) beg-date)
(beg-week (prot-oxford--get-iso-week beg-date))
(end-week (prot-oxford--get-iso-week end-date)))
(when (= term-year year)
(list beg-week end-week))))
(defface prot-oxford-term-indicator
'((((class color) (min-colors 88) (background light))
:foreground "#224499")
(((class color) (min-colors 88) (background dark))
:foreground "#afc9f3")
(t :inherit shadow))
"Face to style the Oxford term indicator.")
(defface prot-oxford-regular-week
'((t :inherit shadow))
"Face to style the regular week.")
(defun prot-oxford-week (month day year)
"Use MONTH DAY YEAR to determine current week.
Derive the Oxford term week based on the `prot-oxford-dates'."
(pcase-let* ((`(,m-w-beg ,m-w-end) (prot-oxford--get-term-weeks 'michaelmas year))
(`(,h-w-beg ,h-w-end) (prot-oxford--get-term-weeks 'hilary year))
(`(,t-w-beg ,t-w-end) (prot-oxford--get-term-weeks 'trinity year))
(gregorian-week (prot-oxford--get-iso-week (list month day year)))
(oxford-week (or (prot-oxford--get-term-week m-w-beg m-w-end gregorian-week "M")
(prot-oxford--get-term-week h-w-beg h-w-end gregorian-week "H")
(prot-oxford--get-term-week t-w-beg t-w-end gregorian-week "T")
"")))
(format " %2s %2s "
(propertize oxford-week 'face 'prot-oxford-term-indicator)
(propertize (format "%2s" gregorian-week) 'face 'prot-oxford-regular-week))))
(defun prot-oxford--get-term-month (term-name term-start-month term-end-month month)
"Return the TERM-NAME of the term month or nil.
Determine the name given TERM-START-MONTH and TERM-END-MONTH as month
numbers. Compare MONTH to them."
(when-let* ((number (cond
((> month term-end-month)
nil)
((= term-start-month month)
1)
((< term-start-month month)
(+ (- month term-start-month) 1)))))
term-name))
(defun prot-oxford--get-months (term)
"Get start and end months of `prot-oxford-dates' TERM as a list."
(mapcar #'car (alist-get term prot-oxford-dates)))
(defun prot-oxford-month (year month)
"Return abbreviated name of MONTH for YEAR.
Append the Oxford term name based on the `prot-oxford-dates'."
(pcase-let* ((`(,m-beg ,m-end) (prot-oxford--get-months 'michaelmas))
(`(,h-beg ,h-end) (prot-oxford--get-months 'hilary))
(`(,t-beg ,t-end) (prot-oxford--get-months 'trinity))
(oxford-term-name (or (prot-oxford--get-term-month "Michael" m-beg m-end month)
(prot-oxford--get-term-month "Hilary" h-beg h-end month)
(prot-oxford--get-term-month "Trinity" t-beg t-end month)
"")))
(format "%s %s %s"
(propertize (calendar-month-name month :abbreviate) 'face 'calendar-month-header)
(propertize (format "%s" year) 'face 'calendar-month-header)
(propertize oxford-term-name 'face 'prot-oxford-term-indicator))))
(defun prot-oxford-intermonth-header ()
"Return string for `calendar-intermonth-header'."
(format "%s %s"
(propertize "OX" 'face 'prot-oxford-term-indicator)
(propertize "Week" 'face 'shadow)))
(setopt calendar-left-margin 10
;; Oxford assumes Sunday starts the week, but we want to work
;; with the ISO commercial dates, so Monday (1) is the first
;; day of the week. But Sunday (0) will still work.
calendar-week-start-day 1
calendar-intermonth-spacing 12
calendar-intermonth-header (prot-oxford-intermonth-header)
calendar-intermonth-text '(prot-oxford-week month day year)
calendar-month-header '(prot-oxford-month year month))
fontaine package,
the way I implement changes to fonts is done via a custom theme.
commit 69e80d4a93b28804f3b9d8a0b4328952c2f0d568
Author: Protesilaos Stavrou <[email protected]>
Date: Wed Jan 8 10:42:35 2025 +0200
BREAKING use a custom theme instead of 'set-face-attribute' internals
fontaine.el | 179 +++++++++++++++++++++---------------------------------------
1 file changed, 63 insertions(+), 116 deletions(-)
Before, I was relying on the internals of set-face-attribute which
worked decently for the most part but required manual intervention to
persist the face attributes between theme changes. Whereas the custom
theme shall remain in effect no matter what, thus reducing complexity.
Furthermore, the custom theme allows me to declare the display specification in which the given face attributes (i.e. the font styles) apply. I can thus specify that these are for a graphical Emacs frame only.
A potential advantage is the ability to modify any face, even if it is
not initialised, whereas set-face-attribute requires the face be
defined, else it produces an error. This potential is not realised for
the time being because there is no face of the sort that Fontaine
affects. All the faces it modifies are loaded eagerly by Emacs. If I
need to cover more faces though, it will be straightforward.
For users, the only obvious effect of this transition is the discontinuation of the option to set a Fontaine preset per frame. All face attributes are now always applied to all frames. I am doing this because the old design did not work reliably in all cases and was a niche feature anyway.
I am not aware of any regressions, though I continue to test the package. If you do try it before the new version is out, please let me know of any possible bugs or other improvements we can make.
Fontaine allows the user to define detailed font configurations and set
them on demand. For example, one can have a regular-editing preset
and another for presentation-mode (these are arbitrary, user-defined
symbols): the former uses small fonts which are optimised for writing,
while the latter applies typefaces that are pleasant to read at
comfortable point sizes.
fontaineM-x
spacious-padding-mode. Adjust the exact spacing values by modifying
the user option spacious-padding-widths.
Inspiration for this package comes from Nicolas Rougier’s impressive
designs and Daniel Mendler’s
org-modern package.
spacious-paddingRelease notes below.
This is a small release that makes some minor refinements. The package is otherwise stable and works as expected.
spacious-padding theme instead of userThis is an internal detail with how we implement the “spacious”
changes to the faces we cover. Before, we would override anything that
the user would explicitly configure with custom-set-faces, either
via their Elisp code or through the Custom User Interface and the
snippet it automatically generates.
By storing our changes in the bespoke spacious-padding theme instead
of the special user theme, we do not override the user’s preference.
This is the right design in principle, though users may no longer get
the same styles as before because faces they had configured before
will now override what spacious-padding-mode does.
When in doubt, search your configuration for custom-set-faces,
comment out the whole block, try to re-enable spacious-padding-mode,
and see if the results are okay.
Thanks to Martin Marshal for making the initial change in pull request
26: https://github.com/protesilaos/spacious-padding/pull/26. The
change is within the 15-line limit of permitted changes without the
need for copyright assignment to the Free Software Foundation. I then
followed it up with the creation of the ~spacious-padding theme.
tab-line-tab-current face is supportedThis means that tab-line-mode will look consistent when
spacious-padding-mode is enabled, instead of some tabs looks
smaller/different.
spacious-padding-mode works with window-divider-modeThanks to Pierre Baille and Tobias Tschinkowitz for bringing up the problem in issue 17: https://github.com/protesilaos/spacious-padding/issues/17.
]]>In this ~21-minute video I demonstrate the new optional extension for
Denote that I am developing. The denote-sequence.el is part of the
denote package. Its purpose is to streamline the creation of
“sequence notes”, i.e. files that have an inherent relationship
between them. The sequence is represented as a number.
Note that the code is in-development and will be released as part of Denote version 3.2.0. I still plan to make some refinements, though the core functionality is already done.
Denote is a simple note-taking tool for Emacs. It is based on the idea that notes should follow a predictable and descriptive file-naming scheme. The file name must offer a clear indication of what the note is about, without reference to any other metadata. Denote basically streamlines the creation of such files while providing facilities to link between them.
Denote’s file-naming scheme is not limited to “notes”. It can be used for all types of file, including those that are not editable in Emacs, such as videos. Naming files in a consistent way makes their filtering and retrieval considerably easier. Denote provides relevant facilities to rename files, regardless of file type.
denote