Python tool for templating and managing dotfiles

I always told myself I would never release this code. For some time I considered the appearance of my desktop environment a form of creative expression, and by putting the tooling I wrote into the hands of others, I might dilute, somehow, the quality of what I had set out to create. As embarrassing as it is to admit this - after all, the purpose of this work was to create a desktop environment that was functional and aesthetically pleasing, and had nothing to do with anyone else's desktop - I hope that by stating my thought processes I can convey how much I had invested in this project.

In this post I'll describe the tooling I wrote to automate the process of adjusting the appearance of the various software I use on a daily basis. Below is a screenshot of my desktop at the time of writing, with a couple applications running:

photos/s1486955078.02.png

At a high level, there's nothing unique about my tools versus some of the other template-rendering scripts. The basic idea is the same: given a context, which contains a mapping of variable name to value, take a template file and replace all instances of a variable with it's corresponding value. It's the details of how these scripts work that, in my opinion, makes them particularly useful.

Config values are dynamically resolved

By this I mean that the values associated with a given configuration key can be variables. In the example below, note how the key foreground refers to white (which occurs later in the config) - this kind of thing is no problem:

settings:
  foreground: $white
  red: '#8c3333'
  white: '#fefefe'
  openbox:
    active:
      label: $foreground
      titlebar: $red
    inactive:
      label: $openbox.active.label
      titlebar: $openbox.active.titlebar

As you can see, the values associated with a given key support variables, which are recursively resolved. This is pretty neat and leads to a lot less typing and mistakes!

Master config vs theme configs

In addition to using variables in the configuration file, the tools also differentiate between the master config file and the individual theme configs. By doing some clever work when you save a theme's config file, the tooling only stores values that differ from those in the master config. Essentially, each individual theme stores only a diff against the master config. Here's the kicker: whenever the master config changes, the individual themes will pick up that change (unless they specifically override whichever keys were updated in the master).

In this way, I can make big changes to the look-and-feel of all my individual themes by editing a single file. But I can also choose to override the defaults in any way in the actual theme configuration.

Theme generation from Xresources file

Suppose you're cruising r/unixporn and someone posts the dotfiles to a theme you think is really nice. You'd like to keep the look-and-feel of your own setup, but would like to incorporate the colors they used. Well, you can simply pass in an .Xresources (or .Xdefaults) file and the script parses out the color values and uses them to populate the colors in a new theme config file. This makes it super easy to try out new colors without having to change up everything in your setup.

It might seem like a limitation that the tool only supports YAML or Xresources, but I've had good luck following a unix-ey philosophy and writing other tools to target Xresources as their output format. For example, I have one script that accepts an image file as input, and outputs 16-colors conforming to a linux terminal color scheme. Another script then takes those colors and turns it into an Xresources-formatted file. Etc. (See end of post for code to these scripts).


Beyond these two features, the rest of the code is a fairly straightforward Jinja2 template renderer wrapped up in some nice APIs. There are also some cool built-in Jinja filters you can use in your templates to do things like:

Since it's Jinja, you get all the built-in features like template inheritance / fragments, control structures (for loop, conditional, etc), and you can write your own macros or filters very easily.

Obtaining and using the tools

The main script is called ta (because it's easy to type) and the code, along with a sample config and templates, is hosted on GitHub at github.com/coleifer/dot-theme. All the config and templates should be placed in $XDG_CONFIG_HOME/ta/ (usually ~/.config/ta). ta has a number of sub-commands which I've found useful, and are described below.

In the screenshot below, I'm using ta to add a new theme named jellybeans, set the current theme to jellybeans, and then list all the themes currently available on my laptop:

photos/p1486952955.44.png

In the following screenshot, I'm using the diff sub-command to show any keys that have been overridden in the new theme. Since the only thing extracted from the .Xresources file were the colors, only the colors are listed in the diff:

photos/p1486952965.48.png

Then, since I'm curious about some other settings, I am using ta show -p <key> to show the values of certain keys (or sub-keys) in the currently-active theme. In the example screenshot, I'm showing the values of the font key, and the openbox.active sub-key:

photos/p1486952972.72.png

There are a few more commands that are also useful:

Editing theme configs

The tools come with a special script, te, which is used to edit the theme configs. It wraps a call to vim and, depending on what changes you make, will automatically re-render the appropriate theme(s). For instance, if I edit the master theme config, te will automatically re-render all the individual theme's templates, because they might need to inherit new values. Similarly, if I edit an individual theme's config, then only that theme's templates are re-rendered.

Check it out!

If you're interested, I encourage you to dig into the source code and make it your own (or just take the parts that interest you and integrate into your own tooling). I've really found it useful to have .Xresources files be the inputs to the theme generator. It's a super easy format to target as the output if you want to write other tools, and it's ubiquitous -- typically someone always includes one when posting their dotfiles.

I hope you found this post interesting, and I hope that the code may be useful to you. Happy hacking!

Comments (0)


Commenting has been closed, but please feel free to contact me