Chris Hannah

Using a Swift LSP in Neovim #

I spent quite a bit of time trying to work out how to get a Swift LSP working in Neovim the other day. Enough time that I wanted to share it here.

I won’t go into too much detail about what an LSP is, how it works, or its benefits. There are a lot of other people who can do a much better job of that than me.

However, I would like to say that this is just how I have it working myself. There are bound to be many other ways you can get a Swift LSP working in Neovim. It just happens that this is a pretty simple configuration, that I will likely also improve in the future.

The first step is to obviously install the Swift LSP. Apple’s one (I’m not sure if there are any others) is SourceKit-LSP. You can build this from source (this is what I did), or the README also explains that it’s also included in the toolchains found on Swift.org. So do whatever is best for you. But, be sure that it is accessible on your $PATH.

After that, you’ll need to configure Neovim to find the LSP, tell it what files to look for (swift), how it can detect the root directory, the name of the command, etc. And then, more importantly, start the LSP, and attach it to the buffer.

My config looks like this:

 local swift_lsp = vim.api.nvim_create_augroup("swift_lsp", { clear = true })
 vim.api.nvim_create_autocmd("FileType", {
 	pattern = { "swift" },
 	callback = function()
 		local root_dir = vim.fs.dirname(vim.fs.find({
 			"Package.swift",
 			".git",
 		}, { upward = true })[1])
 		local client = vim.lsp.start({
 			name = "sourcekit-lsp",
 			cmd = { "sourcekit-lsp" },
 			root_dir = root_dir,
 		})
 		vim.lsp.buf_attach_client(0, client)
 	end,
 	group = swift_lsp,
 })

This is Lua code, so use it in whatever .lua file makes sense to you. However, if you just want to include it in a .vim file, you’ll need to wrap it like so:

" lsp
lua << EOF
-- lua code goes here
EOF

After that, you should be able to write terrible Swift code and have it tell you all the things that you’re doing wrong.

Like this, for example:

swift lsp

For reference, my Neovim config is available on GitHub.


Text Case Is Coming to the Command Line #

If you didn’t already know, I make a text transformation app for iOS, iPadOS, and macOS, called Text Case. It’s grown quite a bit over it’s lifetime, supporting now well over 60 different formats, custom flows, and many more features. And I’ve now decided, the next place I want to bring it to, is the command line

And not just to be an additional benefit of purchasing the existing apps. This is an open-source project, which eventually I want to distribute via Homebrew.

That may sound weird, seeing as you need to pay to have it on other platforms. Why would I release it for free?

Basically, I’m a fan of free and open-source software. And while I don’t think it’s crazy to ask for £2.99 for an app that I’ve put quite a number of hours into making, I do also want to give something back. So, I plan on building a version of Text Case that will allow people to quickly transform text, using the command line.

At the same time, I’m bound to improve my own programming skills, seeing as my code will now be in the public eye. And maybe I’ll learn more about building and distributing open-source software.

However, I do have to say that not all of Text Case will be coming to the command line. I only plan on adding support for the core formats. At least, that’s the plan for now.

Right now, it supports just 17 different formats, and I’ll be working on bringing the rest over as time goes on. And because it’s open-source, you can keep up to date with the current state by checking it out on GitHub. (You can even use it right now, if you’re comfortable with building from source.)

Here’s a quick screenshot of the current version in action:

textcase cli

I hope this news sounds good to at least some of you. In the mean time, I’m going to get back to adding more format options.


Configuring Nginx to Work With Hugo’s “Ugly” URLs #

I recently migrated my blog from running on Ghost to Hugo. With that, came a few changes to how the pages were built, how the URLs were formed, and also the rules around showing 404s and redirecting where possible.

In a default Hugo installation, individual blog posts are each written as index.html files inside a directory named after the given slug. So you would end up with something like so:

blog/
├─ hello-world/
│  ├─ index.html
├─ about/
│  ├─ index.html
├─ my-first-blog-post/
│  ├─ index.html

That means when someone visits either /hello-world or /hello-world/, they’ll be taken to the content (Which is actually stored as /hello-world/index.html).

Just to make things difficult, I didn’t want my file structure to look like that. It felt wrong. Instead I wanted each blog post to be generated as it’s own html file, with the slug being the filename.

Fortunately, Hugo has an option called uglyURLs, which does exactly that. So the same structure above would instead look like this:

blog/
├─ about.html
├─ my-first-blog-post.html
├─ hello-world.html

However, while this may look good to me. It introduced a few issues that I had to deal with. Primarily being that the .html extension had to be given for a page to load. There was no more /about being redirected to /about.html. And definitely no /about/ being supported.

That obviously would break a lot of past links to my blog posts. So I had to find a way to deal with this.

So I set about finding a way in nginx, to essentially remove the need to have the extension in the URL. But I then realised, that it wasn’t that simple.

Because, let’s say I have an about page with the filename about.html. Ideally, I want /about.html, /about, and /about/ to redirect to this page. But at the same time, I have a mini-site for my app, Text Shot, that sites at /text-shot/index.html, and ideally I want /text-shot/index.html, /text-shot/, and /text-shot to all redirect to this site.

That led me to a lot of Googling, regular expressions, and quite a bit of time spent trying various options in my nginx configuration. (If you were super unlucky, you may have hit a 404 while I was testing).

By default, my nginx configuration handled the requests with the try_files function:

try_files $uri $uri.html $uri/ $uri =404;

Basically, if request was /about, it would try to fetch a file from these locations (in order): /about, /about.html, /about/, and then if all of those fail, produce a 404.

The code to deal with removing the need to include .html was relatively simple. All it does is check if the request includes the extension, and if so, remove it and perform a 302 redirect to the plain URI.

if ($request_uri ~ ^/(.*)\.html(\?|$)) {
    return 302 /$1;
}
try_files $uri $uri.html $uri/ $uri =404;

However, that still meant that if you visited /about/, it would think you were looking for /about/index.html, rather than the /about.html page.

So, I had to then add another redirect. So if the url ended in a trailing slash (but wasn’t just /), it would also perform a 302 redirect to a url without the trailing slash. I ended up with this:

location / {
    if ($request_uri ~ ^/(.*)\.html(\?|$)) {
        return 302 /$1;
    }
    if ($request_uri ~ ^/([-a-zA-Z0-9@:%_\+.~?&//=]*)\/$) {
        return 302 /$1;
    }
    try_files $uri $uri.html $uri/index.html $uri/ $uri =404;
}

For reference, $uri is the full request URL, and $request_uri is anything after the host.

There’s bound to be a way for this logic to be improved. But as far as I can see, it works how I expect, and it gives me the exact behaviour I was looking for.

Now for some examples. First off, the ways in which you would get to a file located at /about.html:

(Note: all redirects are stated, the rest is the priority order of the try_files function.)

  1. /about.html: 302 to /about -> try /about -> try about.html.
  2. /about/: 302 to /about -> try /about -> try about.html.
  3. /about: try /about -> try /about.html.

And for the page located at /text-shot/index.html:

  1. /text-shot: try /text-shot -> try /text-shot.html -> try /text-shot/index.html.
  2. /text-shot/: 302 to /text-shot -> try /text-shot -> try /text-shot.html -> try /text-shot/index.html.
  3. /text-shot/index: try /text-shot/index -> try /text-shot/index.html.
  4. /text-shot/index.html: 302 to /text-shot/index -> try /text-shot/index -> try /text-shot/index.html.
  5. /text-shot/index/: 302 to /text-shot/index -> try /text-shot/index -> try /text-shot/index.html.

Note: nginx will search for an index file when try_files checks $uri/, so there’s no need to handle that. However, there were circular redirects when /text-shot/ would be redirected to /text-shot, and then eventually back to /text-shot/, so I added an explicit attempt to $uri/index.html to avoid this.

Admittedly, a few of those examples are a bit odd, and likely will never happen. But I had to cover all bases.

As you can see, it’s not perfect. There are some requests that go through a 302 just to end up at the exact page that was requested. Like #2 above.

In an ideal world, I’ll never touch this configuration again. And there’s also a low chance that anyone reading this would ever need to do anything similar. But, I can’t say I found many resources online for my specific scenario, so I thought I’d write my own.

I guess I brought this all on myself when I decided I wanted to both install my own custom Hugo blog on a linux VM, and to also want static files with the full filename, not just convenient “pretty” URLs that Hugo generates by default. Oh well, at least I learned a bit more about how to configure nginx I guess.


Blog Update #

As of tonight, I’ve now completed the final stage of transitioning my blog from Ghost to Hugo.

As you may have known, my blog has been powered by Ghost for a few years. It’s definitely served me well. However, a while ago I started to like the idea of static sites. Seeing as my blog is essentially a list of static posts, it felt a bit weird that the pages were being served dynamically. So, I started on the journey of moving to Hugo.

It took a bit of work to adapt my existing theme to work with Hugo’s template system. But I managed to get it pretty similar, while also adding a few improvements at the same time.1

Of course, being a static site, pages now load super fast, and they’re extremely lightweight. But that’s not the only good thing about using Hugo. It’s also super easy to host other static pages alongside the source Markdown post files. This means I can build mini-sites for my apps, and keep them managed within my blog. I can control the structure of the site better, either by using categories/tags or by structuring the source files in the way I want them to be generated. And another great one, I can now create custom pages and templates. For example, I built a custom archive page for all of the posts on the blog, and a few extras that as a bit different for essays and travel updates.

In the early version of this new Hugo blog, I had it being deployed to a Digital Ocean app via a GitHub action that was triggered after pushing new posts. But, that didn’t allow me full control of the VM it was running from, so I decided to switch to a droplet (VM), and have built my own minimal installation.

Obviously, Hugo is installed, but apart from that the only other things I had to install was nginx and certbot. So very minimal. I was wondering how I’d manage the deployment, because I still wanted to have the site automatically rebuild after pushing my changes. Luckily, I found a guide by Josh Hausotter that shows you how to configure a remote repository on your Digital Ocean droplet and a “post receive” action that runs a script whenever changes are received to generate the static files and move them to the correct directory. I honestly never thought about using git this way.

As for how I write and publish my posts from my own machine. I do that using Neovim on my Mac, and then just pushing to the remote repository. Neovim might not be the most trendy tool for writers, but I find it works for me, and I also use it for writing code, so I’m pretty comfortable with the keybinds.

You may be wondering, what does this mean for you? Do you need to change anything? Well, in theory, no. The posts are now stored as static html files, however I have configured nginx for these to work without the extension, and the filenames/slugs haven’t changed. Technically, the RSS feed is now different. Hugo generates the RSS feed in an index.xml file at the base of the site, which you can find here. However, I have set up directs for /feed and /rss, so you shouldn’t need to do anything.

Hopefully, this change will go by mostly unnoticed. But if you do notice something odd, you know where to find me. (Links are at the bottom of the page)

Written: while watching Oblivion with my cat.


  1. The theme is named Hurley. (Lost reference) ↩︎


Using Older iPhone Cameras #

I’ve been thinking about the cameras on older iPhones, and what makes people prefer certain models, rather than whatever model is the newest.

A quick answer would be that it’s just subjective, and we all like different things.

But if you think about it, it’s also affected by how people use cameras, what they class as photography, and also what they expect in a photo.

From a photography point of view, it’s easy to imagine that one model had a desired focal length or aperture. And then, if you go deeper into photography, you might be looking for certain colour reproductions, temperatures, tones, lighting, etc. For example, I’m a fan of natural grain in images, so I would prefer one that produces a certain type of grain.

So there’s already a few different metrics that divide opinion. But on top of that, there are a lot of people that use the iPhone’s camera that aren’t photographers.

Some may want the best tool to capture memories, others might just want to document their life, just like the cliché of people posting photos of their lunch to Instagram.

And in those cases, it may seem that the latest model would always be the best answer. But that might not always be true. Recent phones tend to come with a lof of built-in adjustments and corrections that are applied automatically to photos. Therefore, even if you’re aim is to take a photo that documents a certain scene as accurately as possible, the “better” camera might not actually be the best option.

It’s weird to think that a better camera doesn’t always produce better photos.

Written: Sat on my sofa, drinking a cold Coke, listening to Full Moon by The Black Ghosts.


Celebrating Offline Tech #

Maybe it’s just because my own opinions have been changing recently, but I get the feeling that there seems to be a general resurgence of analogue over digital. Film photography is having a moment, so are mechanical watches it seems, and the act of writing in a physical notebook also seems to be growing in recent years. That’s in no way a definitive list, but it’s just a few things I’ve noticed.

This may sound weird, but it feels to me like it’s in some way related to the current period of nostalgia that we seem to be going through as a society. In a lot of ways, it’s like people are trying to bring back the 90s1.

Technology is always going to be massively affected by feelings of nostalgia, simply because of the rate of change that it goes through. However, while there has always been the divide between analogue and digital, there now seems to be a new divide2 in digital technology, offline vs online.

Matt Birchler wrote about this when talking about using 100% offline technology:

There’s no WiFi or Bluetooth, so it’s just out here on its own. It never has updates to install, so it’s never going to get better, but it’s also not going to change in ways I don’t like. It’s also going to work just as well in 20 years as it does today.

As he mentions in his post, while completely offline technology won’t improve, it also won’t get any worse. Which definitely happens to more recently technology that requires a connection to the internet.

I don’t think we’re at some major turning point in society where we’re all going to start writing in paper notebooks, switching to a dumb phone, etc. But it’s worth at least noticing the new era of always needing to be connected3. And at the same time, celebrating the good sides of having technology that can exist on its own, in the same state, for as long as the hardware still works correctly.

Written: In my dimly lit living room, listening to music from Isenseven videos.


  1. I’m not complaining, the 90’s were pretty good. ↩︎

  2. Great song. ↩︎

  3. Both products and people. ↩︎


The More Social Networks There Are the Less I Want to Use Them #

There’s been an influx of new1 social networks recently, such as Threads, Mastodon, and Bluesky. And to some extent, I’ve tried to keep up with them. But if anything, I’ve noticed that my use of social media, in general, has been dropping.

I used to use Twitter for talking about everything, and Instagram for posting my photography. But then Glass came along and for a while my serious photography was going there, and then BeReal made me want to save a photo for them every day. As for text content, there’s been Micro.blog, Mastodon, and Threads that I’ve tried as alternatives to Twitter.

However, all I tend to use now is just Instagram, Twitter/X, and Mastodon. Instagram is where my friends and family tend to be, and that’s where I post photos of what I’m up to, and also any real photography. Mastodon is where I’ve found a lot of tech bloggers and developers have flocked to, so I’m there for that crowd. And I still haven’t given up on Twitter/X, because I’ve found it to still be the best place for current events, football content, and a bunch of tech people are still there. And when I say, I use these three platforms, that’s not the same level as before. I used to try and read every tweet in my timeline, both on Twitter and Mastodon, and I’d spend countless hours scrolling through Instagram.

Now, I’ve got notifications turned off for everything, and I’d say I browse Twitter/X slightly regularly. But I only really go on Instagram and Mastodon now when I want to post something. I haven’t found them to be good places to browse. I get too sucked in when using Instagram, and I haven’t yet cultivated a good enough following list for me to spend a lot of time in Mastodon2.

I’ve found that right now, I’m more interested in people in the real world than on the internet. That’s not a dig at anyone I’ve talked to online. But it doesn’t replace talking to people in the physical world.

I think the reason why I’m preferring to write for my blog over social media, is that it’s a more biased relationship. It allows me to collect my thoughts, and then express them in whatever form I feel fits the content and context. And then if people want to reply in any way, they can do so via email, Mastodon, X, etc. But, at a slower pace, and also in any which way they feel relevant.

The real-time speed and perceived urgency of social media are reasons why I’ve stepped back from it a bit. So, if you’ve sent me a message online or by email, know that I’m probably not ignoring you. I either haven’t got around to reading it yet, or I haven’t yet found time to think and reply.

Written: On a train from London to Kings Lynn.


  1. Well, some aren’t exactly new. But to a lot of people they are. ↩︎

  2. That’s definitely on me. But I’m not particularly interested in spending much time on it. ↩︎


Solo Train Journeys #

People tend to think that I’m a bit weird, because I’m quite fond of a long train journey. Especially when I’m travelling alone. I find it a much more enjoyable experience than any other form of travel.

When I think about why this might be the case, the word that immediately comes to mind is “slow”. But that’s not quite the exact reason. I think it’s because the experience of a long train journey is that it feels slow. Not as in it feels like it’s taking too long, instead, it feels slow because the journey is more relaxed.

This may be just me. But when I get on a train, whether I’m trying to get somewhere urgently like a morning commute or a long journey where there isn’t really any rush, it’s like I’ve given myself an allocated amount of time to do whatever I want.

Let’s say you’re on a 3-hour train ride. You know that you can’t influence the duration, and avoiding any possible delays, you also know the time of your arrival. Which means, for a period of time, you’re free.

You’re free to spend your time reading, watching a movie, listening to music, or even just some time to yourself to sit and think while you look out the window. Better yet, you could do a collection of things.

I tend to use that time to relax, listen to some music, catch up on social media, maybe watch a video or read something, and probably a good chunk of it is spent looking out of the window, while my mind wanders.

I may be alone in this, but a journey in a car or plane is always second best to a train in my opinion. Especially when compared to being on a plane. The whole ordeal of rushing to an airport, going through security, finding your gate, and all of the waiting in between, really bugs me.

A lot of people like to comment on how “chilled out” I am. Like it’s just a part of my personality. But I think it’s more something that I’ve learned to cultivate. Maybe I’m calmer than the average person, but I think it’s decisions like taking the slow option, not rushing myself, or inviting any unneeded stress that makes the difference.

Written: On a train journey from Kings Lynn to London.


I Use Neovim #

I usually tend to write about the tools that I use, whether it’s programming, or writing for my blog. Well, this time, I guess it’s a bit of both. As I’m now using Neovim for practically any task that involves writing text.

I’ve been using it at work, with a recent JavaScript project I’ve been working on, to write quick notes or todo lists, and I’ve also been using it recently to write blog posts.

Because of its extensibility, I’ve managed to adapt it to my own specific needs. I have some basic preferences that you’ll have in most editors like themes, layouts, code highlighting, etc. But I’ve also got a fuzzy finder for files (and buffers), code highlighting/formatting, code completion, and even refactoring functionality.

I’ve only really scratched the surface so far, but I’m already finding it to be an amazing tool. Sure, there’s a steep learning curve. I had to figure out what plug-ins I needed, learn how to configure them, learn [vim motions][vm], and also configure a bunch of key mappings for the specific functions that I want to access in certain contexts. (If you’re interested, my Neovim config is on GitHub)

I’ve definitely learned that it’s not a tool for everyone. Turns out not every developer wants to use or even cares about the command line. I got a few comments like “I just don’t see the point” or “I’m too used to my mouse to use the terminal”.

Maybe it isn’t objectively better than an IDE for writing code, but it certainly feels better to me. Maybe it’s the distraction-free way of working, or that the code is in its most primitive form. But, at least for now, it fits the way I want to work.

And like I said, I’m using it for all sorts of text now. Because if I’m spending most of my time writing code in Neovim, having another plain text todo list in another file is really handy. And once I found myself spending so much time in Neovim at work, when I got home, I started doing the same.

So, when I wanted to write a blog post recently, I decided to try and use Neovim for that. Because after all, my blog is just a collection of static Markdown files that I manage with git. Now, my process of writing a new blog post is to create a new text file, write the post in Neovim, and then use git to commit and push the changes. After that, my site regenerates automatically.

I don’t know how deep I’ll go with Neovim. I still expect any work I do in Java at work to be in IntelliJ, and I can’t see myself using it for my apps either. But for everything else, I think this will be my editor of choice.

And as much as I am enjoying using it, I do find it rather funny that I have a powerful M1-powered MacBook Pro, running a nicely designed, modern operating system, and there I am, with a terminal running full screen and dealing with plain text files.

Next up on my list of things to learn, is tmux. I’ve seen a lot of people use it, and it feels like the logical next step. After that, I want to see if I can build out some of my own Neovim setup with a way to use text snippets, and to output dynamic data such as the curent date.

Now that I’m in this world, I would expect that I’m going to start writing about it a lot more. So you may start seeing the blog sway a bit more technical in the future.


What Sort of Programmer Am I Now? #

I think I may be reaching a bit of an inflexion point, regarding programming, both as professionally, and also as a hobby.

A lot of factors have contributed to this. But I’d say the main ones are the big changes I’ve had in my role at work over the past few years, my feelings about technology and software changing, and also some thoughts on my professional future.

I’ve been at the same company for just over 6 years now, and my role has changed massively. When I was first hired, I was the sole iOS developer, and my primary tasks were to rewrite an existing app in Swift and build another from scratch. (Both of them being clients for financial services).

However, that lasted just over 2 years. It feels pretty weird to say actually. I’ve long thought of myself as an “iOS developer” first, that also has interests in other areas.

Nonetheless, I haven’t really developed for iOS professionally for around 4 years.

I first switched “temporarily” to a big project for the PSD2 regulation changes. That meant a lot of Java (SpringBoot APIs), but I also started working on things like our deployment pipelines, and our infrastructure. It was a sudden change, but I found it pretty interesting.

As you may have guessed, it wasn’t as temporary as expected. Because I continued with various Java projects for quite some time after. The only change was after a takeover, which meant us changing our entire tech stack to something completely in-house. That meant more Java, but instead of REST APIs, it meant switching to internal applications that interacted externally via REST endpoints, but internally via RPC calls.

That has essentially carried on until the start of this year. Which is when I really started wondering about my career in programming, where I want to end up, and figuring out what I need to focus on next. Because I have to say, I’m not the biggest fan of Java. I really didn’t want to think of myself as a Java developer.

Sure, I’ve been working on my own projects in the meantime. Them being apps for Apple platforms in Swift, various websites, blog engines, and blog themes. But I was still writing Java nearly every weekday. So I was becoming a Java developer whether I liked it or not.

One problem I had with it, was that I would be in a weird situation if I were to look for a new job. I haven’t professionally worked on iOS for some time, and I also don’t have the deep knowledge of Java that I would need for a new Java role. I started feeling like I’d either have to quickly get out and focus on iOS again, before finding a new job doing app development, or I’d need to completely pivot and do something new.

That is why I’ve been experimenting a lot this year. Disregarding working on small things like my blog, the projects I’ve worked on in my personal time this year have involved Node.js, Python, Swift, a bit of Rust, and also I’m starting to have a look at Go. I definitely haven’t been making this easy for myself.

Weirdly, it’s been pretty similar in my real job as well. I’ve been given a few projects to work on by myself, one of which, I was in complete control over (It was essentially data manipulation) and I chose to write Python, and another is what I’m working on now, which is an integration for the NetSuite ERP platform in SuiteScript (JavaScript).

We’ve got a relatively small development team locally, and it appears that I’m becoming the guy that does the various bits of work that no one else wants to do. When the idea of writing an ERP integration in JavaScript came up, most people seemed pretty put off by the idea. Because “they weren’t JavaScript developers”. I personally saw it as an opportunity to try something new. Which right now, seems like a pretty good idea. Since I have no idea what I want to do long-term.

I actually joked with someone at work recently about seeing how many programming languages I can use in my job this year. So far it’s been Java, JavaScript, and Python, as I mentioned earlier. But I have my sights set on a target of 5.

We have a lot of work that requires work on our website, and because my team is essentially a bunch of Java developers (minus me), that means dealing with a team in another international office. However, my angle is that I think we should be able to make “small” changes locally. My boss seems to like this idea. So I think it’s quite likely that I will be doing something related to that this year, which means React/TypeScript would be added to the list.

The fifth one I’m not totally sure about. Although, I do want a bit of a challenge, so I may try to push for something like Rust or Go. Either way, I think I better start laying some groundwork soon.

Hopefully, at the end of this year of experimentation, I’ll be a bit closer to figuring out what I want to do professionally. Do I want to really focus on something new like Python or Go? Do I want to lean into Java more? Or possibly do I even try and stay working on as many technologies as I can?

As for right now, I still don’t know the answer to the question in the title of this post. What sort of programmer am I? I’m certainly not just an iOS developer anymore. Am I full-stack? Or haven’t I done enough front-end for that? Is a weird mix of skills what’s now referred to as a software engineer?

I’m starting to think that I’m just a “programmer”. No fancy specifications (or limitations), just someone that writes code in order to get things done.


Archives