(icon courtesy of https://github.com/jaidetree/doom-icon)
What follows is a higgledy mess of Emacs Lisp (no but really). If for whatever reason you find something useful here, it would be nice if you mentioned me when doing so. It’s just good manners :)
Rather than the traditional front-facing blurb coupled with a smattering of text here and there; this setup will try and favour smaller files and lots of prose where needed. This idea falls apart with the modules concept, but generally I don’t have anything too bespoke yet.
As this is my configuration for me, there is at least an assumption of knowledge on my end; but if there is anything here that can be made (constructively) clearer then I’m all ears.
Knowledge is like muffins; best shared around.
The rough structure of what I have is available below, all modules should have some amount of commentary so if you need anything more specific then please look deeper.I aim to comment more in-depth using outline-minor-mode, but for now it’ll do.
The list changes so it would be futile for me to refer to them all here.
A file that gets loaded before both packages and GUI is initialised, so we can set things like the location for elpaca/use-package packages properly. There are also a couple of performance enhancements for speeed and it’s also where we set aEsThEtIcS.We also set as much of no-littering bits as we can to pre-empt it loading later.
This is also where we call our potential init-local.el file which
contains machine-specific config I can’t commit yet.
ui, completion etc. These are denoted by
the filename prefix lkn- and anything else is considered arbitrary
Emacs Lisp.
To make my life easier, there’s not a rigid structure here. If I feel like I’m adding a bunch of packages relating to a specific thing, I’ll move it to a module. I may look at granularity down the line, akin to what Doom Emacs offers, but for my needs now this will do.
Below are a couple of rules I’ve given myself to make writing and maintaining this config a bit simpler. Put simply; keep bindings and setup for a package relative to it where possible. One of the reasons I don’t like maintaining a largeinit.el file is the tendency
to just get into a habit of “throwing code at the bottom” and it getting lost
and hard to find. Whereas for me; it becomes quite simple.
Coupled with some extra additions to imenu to make use-package calls show up, it
becomes very fast for me to identify where something is set.
use-package is a built-in macro for managing packages. An example is included
below that I will run through after.
(use-package package-name
:after some-dependent-package
:custom
(some-custom-value t)
:preface
(setopt some-value-at-byte-compile t)
:init
(setopt some-value-before-it-loads t)
:config
(setopt some-value-after-it-loads t))The above shows an average example package and the structure of it. package-name
is the name of some package from MELPA or elsewhere, and the subsequent keywords
are used to specify rules and conditions for loading the package, and what to do
after.
It would be pointless of me to expand further here, so I encourage you to read the manual for it.
Where the “reasonable” constraint extends to is dumping lots of defun
calls into :init or :config. Doing so makes it much harder to look at
the source, as it pulls the source of the entire form rather than just
the function.
An example of me breaking this rule is the massive (use-package emacs
block, but this is being split out over time as use more of built-in
bits.
This is fine for quickly testing things, it would be quite unproductive of me to have to add a blurb every time I wanted to try out a new package; but at commit-time there should definitely be something.
I’m also breaking this rule a lot, but it’s actually a test for
myself. I plan on adding outline-minor-mode soon, and by having
liberal amounts of comments I’m anticipating that when I add this I
will indeed be able to describe everything.
But over time you find yourself only using a couple of bits and you never bother to study it again until you see someone else using it and you yet again study the manual and pick up a few more.
You spend so long doing this that you don’t actually end up improving much, or worse than this you just never use the package at all. If it’s a core editing package and it’s not being used daily (not something like a google translate plugin, unless translating is something you do all the time…), it should be removed.
This rule, due the the specification of it being “arbitrary”, is less strict than the others; but if I ever find myself going “oh yeah, I remember that plugin being great but I never actually used it”; it can go. Less plugins is less maintenance.
Going with the previous explanation of adding outline-minor-mode also,
when documenting if I hit “oh, I’ve not used this” then I can safely
remove it.
A great example here is plugins like evil-surround. There’s a bunch of them around that implement this, and if you’re not using Evil like I am then you’ll need to find a decent alternative.
Sure you could add in a dedicated plugin like surround or many others .... but smartparens implements the 3 functions for it that I use (add a pair, delete a pair, change a pair). I don’t do anything more complex than that, and with no complex suite of modes to worry about I don’t need a complex plugin anymore either.
And since I already use smartparens, I can cut the dependency.
Think most people running their own config like that is in the similar boat, but over time Emacs has kept up pace with the other editors quite nicely.
Being 40+ years old it of course likely never will be an industry-leader (though that does depend who you ask…) but that doesn’t really matter, it delivers modern features still like tree-sitter (which not even vscode has yet) and LSP (again something missing from vscode).
Unless there’s a specific thing missing from something built-in or it just simply doesn’t exist, then built-in stuff should be preferred. I’ll probably run fast and loose with this rule, but in general less plugins is less maintenance.