I started to use the Zed editor as my default editor, and with it I also use the terminal that comes with it. When using the Zed terminal, I would also like the git commit messages to open in Zed, but only if I’m actually in the Zed terminal. To do this, I first added this to my Zed configuration:
"terminal": {
"env": {
"TERM_PROGRAM": "zed"}
}
And then, in my Fish config, I added this:
# Zed editor
if test "$TERM_PROGRAM" = "zed" set -x EDITOR zed --wait
set -x VISUAL zed --wait
end
Now, only when I’m in Zed terminal, git commits will also open in Zed. Any other terminal, it defaults back to Neovim.
When working on side projects, I prefer setting up a custom domain for each app I’m developing. This approach mimics the production environment more closely and simplifies juggling multiple apps simultaneously.
Using custom domains for local development offers several advantages:
It prevents cookie mixing between different apps.
It allows for easier configuration of CORS policies.
It provides a more realistic testing environment.
Setting this up is straightforward, especially with the help of Caddy. Here’s my process:
First, choose your domain: I typically use the local subdomain. For instance, if my production app is breadandbutter.com, I’ll use local.breadandbutter.com for local development.
Configure Caddy, assuming my local server runs on port 4000, here’s the Caddy configuration I use:
Finally, add an A record for local.breadandbutter.com pointing to 127.0.0.1 in my hosts file or local DNS server.
That’s all there is to it! Now I can access my app at local.breadandbutter.com, enjoying the benefits of a custom domain for my local development environment.
This setup not only enhances my development workflow but also helps catch potential issues related to domain-specific configurations early in the development process.
In my previous note Shorter feedback loops with Elixir tests I already described how to run tests in Elixir, where it only runs the failed tests. Below is what I put in my mix.exs file to have faster feedback loops when testing:
defp aliases do
[
...
"test.failed": ["test --failed --max-failures 1"],
]
end
# Make sure the alias runs in the test MIX_ENV environment.
def cli do
[preferred_envs: ["test.failed": :test]]
end
This will create a new alias test.failed that will run only the failed tests and stop after the first failure. This is useful when you are working on a specific test and you want to run it quickly without running all the tests.
I’m a big fan of Tailscale, and I use it to create my own private network. This means that I can connect to my devices from anywhere in the world without having to worry about opening ports or exposing my IP address. I recently also used Tailscale to connect to a PostgreSQL server; below is how I did it.
To get Tailscale working on my Ubuntu machine, I first needed to install the Tailscale client. I did trust the Tailscale team and used their installation script:
curl -fsSL https://tailscale.com/install.sh | sh
Then, I needed to configure Tailscale to connect to my account with:
sudo tailscale up
Tailscale is now up and running. Next, let’s open up the PostgreSQL port on the Taiscale network. I’m running the UFW firewall, so this is what I did:
sudo ufw allow in on tailscale0 to any port 5432 proto tcp
Now, I also need to make sure PostgreSQL runs on any interface. I did that by changing the listen_addresses in my postgresql.conf file:
listen_addresses = '*'
In my pg_hba.conf file, I added the following line to allow connections from Tailscale. I decided to use the trust method because if somebody can connect to my Tailscale network, I’m already screwed anyway.
host all all 100.64.0.0/10 trust
And then restarting PostgreSQL:
sudo systemctl restart postgresql
When I’m on my laptop and I’m connected to the Tailscale network, I can connect to my PostgreSQL server with:
psql -h <name-of-your-server> -U postgres
The <name-of-your-server> is the name of your Tailscale device. You can find it in the Tailscale dashboard. It’s easier to remember than the IP address.
That’s all there is to it! Now you can securely connect to your PostgreSQL server from anywhere in the world using Tailscale’s private network. You can use this technique to expose multiple applications across your machines. For example, I’m also using it to connect to my Raspberry Pi, where I’m running PiHole, a DNS ad blocker.
Wanting to try out some command line tools written in Python, I needed to install Python on my Ubuntu machine. Since it’s more than a decade without Python, I needed to lookup how that was done these days. This is what I ended up with:
And apparently, these days you can’t just install packages with pip unless you are in a virtual environment. So now you have pipx, which I used to install ruff.
In Clojure, “Rich Comment Blocks” serve as both a playground and a log during development. Named after Clojure’s creator, Rich Hickey, these blocks offer a unique approach to REPL-driven development.
These comments are rich because they provide rich detail about the development process and because they were written by a person named Rich.
Rich Comment Blocks look like this:
(comment
(println "foo"))
When the program runs, it ignores the top-level form. However, during development, you can evaluate each line individually, making it an invaluable tool for experimentation and debugging.
As a Common Lisp user, I initially missed this feature. While Common Lisp does have the #+nil reader macro that can be used for similar purposes, it’s not quite as flexible as Clojure’s comment form. The #+nil macro looks like this:
#+nil
(print "This won't be evaluated when the file is loaded")
However, I wanted something closer to Clojure’s Rich Comment Blocks, so I created my own comment macro in Common Lisp:
This simple macro brings the power of Rich Comment Blocks to Common Lisp, offering more flexibility than the #+nil reader macro. It allows you to wrap multiple expressions and even nested forms, which can be selectively evaluated during development.
Give it a try - you might find it as useful as I do.
Recently, I was exploring the Spritely Institute’s blog and discovered their project, Guile Hoot. This Scheme to WebAssembly compiler enables running Scheme code directly in web browsers, opening up new possibilities of using Scheme.
While the project’s documentation is tailored for Guix users (a natural choice for Guile enthusiasts), I’m stuck on MacOS and needed to adapt the setup process using Homebrew. To save you time and frustration, this is how I got it running:
First, install the latest version of Guile using Homebrew:
# This may take some time
brew install guile --HEAD
Next, set up the necessary environment variables. For Fish shell users like myself:
set -x GUILE_LOAD_PATH $brew_prefix/share/guile/site/3.0
set -x GUILE_LOAD_COMPILED_PATH $brew_prefix/lib/guile/3.0/site-ccache
set -x GUILE_SYSTEM_EXTENSIONS_PATH $brew_prefix/lib/guile/3.0/extensions
For Bash or ZSH users, use the following (replace brew_prefix with the actual path or set it as a variable):
Now, let’s clone the Guile Hoot repository, configure, and compile it. Ensure that the prefix directory matches your brew_prefix:
git clone https://gitlab.com/spritely/guile-hoot.git
cd guile-hoot
./bootstrap.sh
./configure --prefix=/opt/homebrew
make
# No sudo needed when using Homebrew
make install
To verify your installation, launch the Guile REPL and test if the following command loads without errors:
scheme@(guile-user)> ,use (hoot compile)
If you don’t encounter any errors, congratulations! You’ve successfully set up Guile Hoot on your Mac. Now, let’s see if I can create a game in the browser with Scheme and WASM!
Recently, I stumbled upon a Reddit thread on font rendering in Emacs on macOS. I figured many users might be unaware of simple trick for crisper fonts:
As an Emacs enthusiast, I’ve found Mu4e to be an excellent email client. While Emacs can send emails, it can’t fetch and sync them efficiently. Enter isync, the perfect companion to fill this gap.
Setting up this duo took some digging, so I’m sharing my configuration to save you time and headaches.
Here’s what my setup accomplishes:
Syncs all mailboxes, excluding junk and notes.
Renames “Sent Items” to “Sent” for simplicity.
Encrypts your password on disk using GPG.
First, I recommend to install the latest version of isync, if you are on the Mac, use brew install isync --HEAD. Then, you’ll need to create an encrypted password file. In Emacs, run the epa-encrypt-file command.
Now, let’s dive into the heart of the setup. Here’s my .mbsyncrc configuration file:
# Petar account on Fastmail.
IMAPAccount petar
Host imap.fastmail.com
Port 993
AuthMechs LOGIN
User [email protected]PassCmd "gpg -q --for-your-eyes-only --no-tty -d ~/.mbsync-password-petar.gpg"TLSType IMAPS
TLSVersions +1.2
IMAPStore petar-remote
Account petar
# Petar Local.
MaildirStore petar-local
Path ~/Mail/Petar/
Inbox ~/Mail/Petar/Inbox
Trash ~/Mail/Petar/Trash/
SubFolders Verbatim
# Sync everything besides sent and junk.
Channel sync-petar-all
Far :petar-remote:
Near :petar-local:
Patterns * !"Sent Items" !"Junk Mail" !"Notes"Expunge None
CopyArrivalDate yes
Sync All
Create Near
SyncState *
# Sync and rename the sent items
Channel sync-petar-sent
Far :petar-remote:"Sent Items"Near :petar-local:Sent
Expunge None
CopyArrivalDate yes
Sync All
Create Near
SyncState *
Group petar
Channel sync-petar-all
Channel sync-petar-sent
With this setup, you’ll have all your email on disk, ready to use it in Emacs and Mu4e.