Resources

Air - A blazingly fast R code formatter (Davis Vaughn & Lionel Henry, Posit) | posit::conf(2025)

Air - A blazingly fast R code formatter Speaker(s): Davis Vaughan; Lionel Henry Abstract: In Python, Rust, Go, and many other languages, code formatters are widely loved. They run on every save, on every pull request, and in git pre-commit hooks to ensure code consistently looks its best at all times. In this talk, you'll learn about Air, a new R code formatter. Air is extremely fast, capable of formatting individual files so fast that you'll question if its even running, and of formatting entire projects in under a second. Air integrates directly with your favorite IDEs, like Positron, RStudio, and VS Code, and is available on the command line, making it easy to standardize on one tool even for teams using various IDEs. Once you start using Air, you'll never worry about code style ever again! posit::conf(2025) Subscribe to posit::conf updates: https://posit.co/about/subscription-management/

image: thumbnail.jpg

Transcript#

This transcript was generated automatically and may contain errors.

So as we get started here, I just want to have you look at this big wall of text. Now this is just hard to read. That's the main thing I want you to get out of this. Particularly when compared to this on the right.

Now over here we've got great use of indentation, whitespace, formatting, style. It just looks more pleasurable to read.

And this concept doesn't just apply to prose. It also applies to code. For example, I'm sure we've all seen code like this. This is a big for loop. It's very compressed. If I were to ask you what this does very quickly, it would take you a couple seconds to kind of figure out what it's doing. Maybe longer.

Compare that to this. This is a formatted for loop, which is the exact same content. But it's much easier to see that all this does is print out even or odd based on the number.

I think we can all agree that this looks better. The problem is that just getting to this form is sometimes really tedious.

For example, here I have a pipe chain. This pipe chain extends off the right hand side of the screen. Watch me struggle to format this thing. I might first work on aligning the pipes. And then I might decide, well actually I want this pivot longer call to be expanded as well. So I spend some time doing that too.

And again, I think we can all agree that this looks better. But at the end of a long workday, I'm going to start doing this manual work less and less.

So let's reset. Wouldn't it be cool if there was a tool where you could snap your fingers and the pipes would realign? Or maybe you could snap your fingers again and it would expand your pivot longer call automatically.

If that sounds interesting to you, then you will be very interested in Air. Air is an extremely fast R code formatter. If you've never heard of a formatter before, it takes messy code and turns it into pretty code, just like you saw on the previous slide. And when hooked up to your editor, Air gives you a number of formatting superpowers, which we'll be looking at in this talk.

Setting up Air in a project

Now, if you're thinking to yourself, like, is this just like one more tool that I have to learn? Is this a lot of things for me to have to set up in my project? Know that we have made setting up Air in an existing project as easy as possible. It's actually only two steps. You add some configuration that tells your IDE how to use Air, and then you format all of your files.

In fact, it's so easy, we're just going to go ahead and show you how it works. We're going to jump over into Positron here. And what you need to know about Positron is that it already comes bundled with Air. There's actually nothing else you need to do to install it. You may have it, and you didn't even know.

So we're going to do two things in this project. There's three R files here. We're going to add that configuration we talked about, and then we're going to format these three R files. To add the configuration, all we do is run this command, initialize workspace folder. And over here, you can see a couple of configuration files have been added. All these do is tell Positron to use Air to format your file.

Go ahead and commit those. The next step is to actually format these files using Air. You see that button in the bottom right-hand corner? Click to format the workspace folder. Click to format the workspace folder. If we click that, then magically, all of our files have now been formatted with Air.

If you use Git, this is a great chance to kind of look and see what Air has done in terms of, like, remote formatting the code to make it pretty. But if you don't care, you can just commit and say, OK, looks good, formatted all the files.

So that's all it takes to actually get set up to use Air. But that's not the fun part. The fun part is using Air on a day-to-day basis. And again, if you're thinking, is this one of those tools where I have to learn a whole bunch to use this thing? Well, actually, we believe that if you know how to save a file, you already know how to use Air.

Format on save

We're going to look at that next. We'll jump over into a file here called visualize.r, another file in our project. We've got some ggplot2 code that's incomplete, so we'll go ahead and finish it out. Plus geoblind, maybe, and theme minimal.

And the key here is that as I'm just trying to get this out of my head, I don't really care what the code looks like. That's not what I'm thinking about at the time. But now that I have gotten it out of my head, I take a breath, and I save the file, and Air kicks in, and it reflows your pipes automatically.

Or maybe I decide, well, actually, I'm not ready to do my plots yet. I need a couple extra columns. So I might do this mutate call and add a few extra columns on. And again, as I'm typing this, I'm not really thinking about what the code looks like. I just am trying to get this out of my head so that it's on the screen here. But now that I have, I take a breath, and I save, and Air kicks in again, and Air kicks in again, reflowing the pipe, reflowing the mutate call, adding whitespace, generally making things look pretty automatically.

You might hear that I'm saying I'm doing this on save, and, you know, I save hundreds of times a day. So you might be wondering, is this going to slow you down at all? You know, you save a lot. One of our goals with Air is to be imperceptible with format on save. It should be so fast that you question if it even ran.

One of our goals with Air is to be imperceptible with format on save. It should be so fast that you question if it even ran.

To put some concrete numbers behind this, we'll look at dplyr's across.r. This is a big thousand-line R file, so pretty big. It takes Air around 20 milliseconds to format this file. To put that in perspective, if you blink, it takes about 200 milliseconds. So it's extremely fast.

If we were to scale up, all of dplyr has about 150 R files in it. If you were to format the whole project, it takes 200 milliseconds. You can format all of dplyr in the blink of an eye.

Now, if you're on the border of deciding, you know, should I use Air or should I not in my project, know that if you do, you will be in good company. Pretty much all of the packages in RLib and the tidyverse at this point use Air. We've run it over hundreds of thousands of lines of R code at this point, and we feel very good about it. In fact, just this morning, I removed the beta label from Air. So if that's been holding you back, you should feel free to use it at this time.

Reducing collaborative friction

So our goal with the formatter is that once you start using it, it will be hard for you to go back to your formatter-less lifestyle. And that's something we've certainly managed to achieve with Hadley, because now when he saves a file and he does a new format, it feels broken for him. And we kind of knew that Hadley would be an easy win for us because he has always cared a lot about style.

But now I would like to talk a little bit about what happens when you have a room full of programmers who all care a lot about style. Something like this happens.

So these are excerpts from our GitHub organization where a pull request came in, and the reviewer is focusing part of the review on the style of the code rather than on the contents. So that's not a good place to be in, and we call this collaborative friction.

And the nice thing about the formatter workflow is that if everyone on your team gets on the same page and is using the formatter as configured in a project, and the formatter kicks in every time a file is saved, well, the code is never going to go out of sync with the expectations around the style. And so that fully solves the problem of collaborative friction that we saw in the last slide.

So you might think, but what about an external contributor? They might not be using the formatter. Well, for this scenario, we have a GitHub action that you can set up in your repository. And when you do, a robot will come in for every contribution and check the style for you. And it will even suggest fixes when something is not right, and the contributor can merge the fixes by themselves. And so there's no human in the loop anymore for checking the style.

And so hopefully, when the reviewer comes in, they can focus on the contents of the code and get the contribution much faster. So now there's another aspect to collaborative friction, and that has to do with configuration.

And to see this, here's an example of a configuration file for a formatter called clang-format. And that's for C code. And as you can see, it has a lot of options, extremely configurable. And that's only a small sample of the option that you can set to specify how you want your code to look.

And that's how you want your code to be formatted. So when it comes to configure the formatter for a project, that's a lot of complexity. And maybe some of you would appreciate this flexibility, but we would like to argue that there is a hidden cost to it.

And to see that, let's see an example with Davis and I trying to configure a formatter for Rust code, because we write a lot of Rust code these days. Suggesting that we might be able to use a particular option. But then I come in, and I'm like, well, actually, I don't like that option. I think it's better if the code looks this way. And then we're just back here again. So we're back to collaborative friction, because now we have to agree as a team on all the options that we want to use in the project.

So that's why we made the formatter more like a live switch. It's either on or off. You're either using the formatter with all the rules that we have implemented, or you're not.

And our configuration file looks more like this. So you can decide how wide you want your code to be on average. And you have a couple of options about indentation. So whether you want to use tabs or spaces, and how wide an indentation should be.

Controlling code layout

So we have kind of a strict formatter. But does that mean that you have absolutely no say as to how the code is laid out? With the formatter. Well, not quite, because one of our goals was to give you power and control over how the code is laid out. And this happens in two ways.

And the first way is persistent line breaks. And here we have an example with a couple of expressions, a list call and a ggplot pipeline. And they are laid out in what we call the flat layout, which is like horizontal. And let's say that you, as a programmer, want to go to the expanded layout, which is more vertical.

The idea of persistent line breaks is that you are going to find the first element in that expression, insert a line break, save the file. And it's going to be taken as the line break is an instruction for the formatter to go to expand it. So let's see what it looks like. With the list call, you can find the first argument, insert a line break, you save, goes to expanded. And with the ggplot pipeline, you find the first element, insert a line break, you save, and it goes to expanded.

And the nice thing is that this process is reversible, so you can remove the line break, you save, it goes back to flat. And same with the list call. So that's one of our favorite features with the formatter because it really gives you a grip on your code. And we hope that you will like it too.

So the second way you have control is with magic commands. So here we have an example with the igraph package, which is a package to work with graphs. And it has a domain-specific language to specify graphs with plus and minus operators. And if you try to use the formatter with this code, it's not going to know how to do a good job with this code. And you get something that doesn't look like a graph anymore.

So what you can do in these situations is use a magic command with a skip directive. And that will instruct to ignore the next expression. So if you use igraph, you're not going to want to sprinkle your code with magic commands. So there's also a way in the configuration file to specify which function should be skipped across the whole project.

Now, magic commands are not only for turning off the formatter. And we are starting to look into new ways to specify how to layout code. And one feature that is currently in development is a tabular directive. And that's going to be extremely useful with Tribble calls, for example.

So if you don't know, Tribble is a tidy-verse way of creating a data frame where you enter tabular data in your script. And historically, it's always been kind of a pain to format these Tribble calls because you have to manually align all of the columns. But with the formatter, that becomes automatic. You just save the file and everything goes in the right place. And if you edit one of the fields, create some misalignments, you only need to save the file and everything is perfect and you get a nice little Tribble call.

So that feature is still in development but coming soon to the formatter. But as you can see, even though we have a strict configuration, we still give you many ways to control how the code should be laid out.

So in general, we think that the formatter will save you time, that it will make your code pretty, that it will reduce collaborative friction, and in general, just spark joy in your development workflow.

IDE support and availability

So as we wrap up here, we actually have one more thing that we wanted to show you. Now, all the examples and videos that you've seen so far have been in Positron. But the cool thing about Air is that it's actually an external tool that Positron uses. What that means is that it can also be used in RStudio.

And in fact, it can be used in any IDE. NeoVim, Zed, Helix, VS Code, Positron, RStudio. Because we use the industry standard language server protocol, it means that Air can be used anywhere.

You see, we want Air to be a tool for the entire R community. And this means providing great support for all IDEs, not just Positron and RStudio. Because at the end of the day, Air is for everyone, no matter where you write your R code. Thank you very much.

Q&A

So, should we switch away from Stylar to Air and why? Yes. We have talked to Lorenz, who is the current maintainer of Stylar. He is very happy with where Air is at this point. It is over 100 times faster. The performance is a feature. When you save with Stylar in large files, it takes over a second sometimes to save the file. When you do that hundreds of times a day, it does slow you down.

We think the performance is a feature and we think you should switch. And also, in Stylar, there is no line splitting, like going to the expanded layout. So, that's a limitation that the new format solves.

How did you manage to make Air so fast? The one word we haven't said, Rust. That's it.

The one word we haven't said, Rust.