Configuration
Air can be configured using a TOML file named air.toml. Air is purposefully minimally configurable, with the main configuration points being related to line width and indent style.
Example configuration
Below is a complete air.toml file showing all available options set to their default values:
[format]
line-width = 80
indent-width = 2
indent-style = "space"
line-ending = "auto"
persistent-line-breaks = true
exclude = []
default-exclude = true
skip = []
table = []
default-table = trueConfiguration recommendations
For collaborative projects, we recommend creating an air.toml and placing it at your project root even if you plan to use the default Air settings. The easiest way to do this is by running the VS Code or Positron command Air: Initialize Workspace Folder, or by running usethis::use_air() if you are using another IDE.
The existence of an air.toml has a number of benefits:
It stops Air from using user level editor settings. When an
air.tomlis not present, Air respects some user level editor settings, such aseditor.tabWidthin VS Code or Positron. The presence of anair.toml(even an empty one) disables this, instead pulling all settings from the configuration file. This ensures that settings are the same across team members, code editors, and in CI.It stops configuration discovery from extending outside your project. Without an
air.tomlat the project root, Air might discover your local~/packages/air.tomlas the configuration file to use for the~/packages/dplyrproject. By adding~/packages/dplyr/air.toml, you prevent configuration discovery from looking “above”~/packages/dplyr, again ensuring consistent settings across team members.It serves as a signal to others that your project is using Air.
If you do plan to just use the defaults, the air.toml can be completely empty. The important thing is that it exists.
If you’re happy with Air’s defaults (or your IDE settings, see synchronization) and don’t plan for your project to ever leave your computer, you likely don’t need an air.toml.
Configuration discovery
The ideal place to put an air.toml file is at your project root. For example, note the placement of air.toml in this minimal dplyr project:
~/packages/dplyr
├── air.toml
├── DESCRIPTION
├── NAMESPACE
├── R
├── src
├── tests
└── vignettesIf you run air format with a working directory of ~/packages/dplyr or open your IDE in the dplyr project, then Air will find and use that TOML file.
Air also supports walking up the directory tree from the project root. For example, if you ran air format from within ~/packages/dplyr/R, then Air would look “up” one directory and would find and use ~/packages/dplyr/air.toml.
Settings synchronization
In IDEs that support synchronization (VS Code and Positron currently), Air does its best to ensure that the formatter and the IDE are in agreement. This is supported by two mechanisms:
In projects that don’t have an
air.tomlfile, IDE settings are sent to Air. This ensures that Air will use the same indentation style and width that is configured in the IDE.In projects that do have an
air.tomlfile, the Air settings are sent to the IDE. This ensures that the IDE will use the same indentation style and width that Air uses.
The goal of this synchronization mechanism is for the IDE to work in lockstep with the Air formatter.
Dotfiles
Air supports both air.toml and .air.toml. If both are present in the same directory, then air.toml is preferred (but we don’t recommend this).
Format options
All formatting options are specified under the [format] table.
line-width
The preferred maximum line length.
An integer value between 1 and 320, with a default of 80.
While the formatter will attempt to format lines such that they remain within the line-width, it isn’t a hard upper bound, and formatted lines may exceed the line-width.
indent-width
The number of spaces per indentation level.
An integer value between 1 and 24, with a default of 2.
This option changes the number of spaces the formatter inserts when using indent-style = "space". It also represents the width of a tab when indent-style = "tab" for the purposes of computing the line-width.
indent-style
Whether to use spaces or tabs for indentation.
One of the following values, with a default of "space":
"space": Use spaces for indentation."tab": Use tabs for indentation.
Air defaults to spaces due to the overwhelming amount of existing R code written in this style, but consider using tabs for new projects to improve accessibility. See indent-width to configure the number of spaces per indentation and the tab width.
line-ending
The character air uses at the end of a line.
One of the following values, with a default of "auto":
"auto": The newline style is detected automatically on a file per file basis. Files with mixed line endings will be converted to the first detected line ending. Defaults to\nfor files that contain no line endings."lf": Line endings will be converted to\n. The typical line ending on Unix."crlf": Line endings will be converted to\r\n. The typical line ending on Windows."native": Line endings will be converted to\non Unix and\r\non Windows.
persistent-line-breaks
Whether or not persistent line breaks are allowed.
Either true to respect persistent line breaks, or false to ignore them, with a default of true.
Air respects a small set of persistent line breaks as an indication that certain function calls or function signatures should be left expanded. For example, the following list could be flattened to one line and would still fit within a line-width of 80, however, it remains expanded due to the persistent line break between the opening ( and the first argument, apple.
dictionary <- list(
apple = 0.75,
banana = 0.25,
cherry = 0.50
)Similarly, this function signature could also be flattened, but is not, due to the persistent line break between the opening ( and the first parameter, ....
case_when <- function(
...,
.default = NULL,
.ptype = NULL,
.size = NULL
) {
body
}To request flattening in these cases, just remove the persistent line break. For example:
# If you started here,
dictionary <- list(
apple = 0.75,
banana = 0.25,
cherry = 0.50
)
# then do this, and run air,
dictionary <- list(apple = 0.75,
banana = 0.25,
cherry = 0.50
)
# to get this.
dictionary <- list(apple = 0.75, banana = 0.25, cherry = 0.50)Alternatively, use a tool such as codegrip bound to a keyboard shortcut to flatten the code, and Air will keep it flattened as long as it fits within the line-width.
It may be preferable to ignore persistent line breaks if you prefer that line-width should be the only value that influences line breaks.
exclude
The set of additional files and folders to exclude.
A list of strings, i.e. exclude = ["file.R", "folder/", "files-like-*-this.R"].
By default, Air will refuse to format files matched by patterns listed in default-exclude. Use this option to supply an additional list of exclude patterns.
Exclude patterns are modeled after what you can provide in a .gitignore, and are resolved relative to the parent directory that your air.toml is contained within. For example, if your air.toml was located at root/air.toml, then:
file.Rexcludes a file namedfile.Rlocated anywhere belowroot/. This is equivalent to**/file.R.folder/excludes a directory namedfolder(and all of its children) located anywhere belowroot/. You can also just usefolder, but this would technically also match a file namedfolder, so the trailing slash is preferred when targeting directories. This is equivalent to**/folder/./file.Rexcludes a file namedfile.Rlocated atroot/file.R./folder/excludes a directory namedfolder(and all of its children) located atroot/folder/.file-*.Rexcludes R files named likefile-this.Randfile-that.Rlocated anywhere belowroot/.folder/*.Rexcludes all R files located atroot/folder/. Note that R files in directories underfolder/are not excluded in this case (such asroot/folder/subfolder/file.R).folder/**/*.Rexcludes all R files located anywhere belowroot/folder/.**/folder/*.Rexcludes all R files located directly inside afolder/directory, where thefolder/directory itself can appear anywhere.
See the full .gitignore documentation for all of the patterns you can provide.
default-exclude
Whether or not the default file exclude patterns are used.
Either true to use the default exclude patterns, or false to not use them, with a default of true.
By default, Air excludes a set of files and folders that you are unlikely to want formatting in. The complete list of default exclude patterns is:
.git/renv/revdep/cpp11.RRcppExports.Rextendr-wrappers.Rimport-standalone-*.R
skip
The set of function names to skip formatting for, even without a # fmt: skip comment.
A list of strings, i.e. skip = ["my_dsl_function", "graph_from_literal"].
Air typically formats every function call it comes across. To skip formatting of a single function call, you can use a # fmt: skip comment. However, if you know of particular functions that you use a lot that are part of a custom domain specific language that doesn’t follow conventional formatting rules, you can entirely opt out of formatting for those functions by providing them here.
For example, using skip = ["graph_from_literal"] would automatically skip formatting of:
igraph::graph_from_literal(A +-+ B +---+ C ++ D + E)table
Some function calls are meant to be formatted as tables, rather than being formatted in a flat or expanded layout. For a single one-off function call, you can use a # fmt: table comment to request a table layout. For function calls that you use a lot, use this setting to add them to a list of function calls that are automatically formatted as tables without requiring a # fmt: table comment.
For example, using table = ["my_table"] would automatically format calls to my_table() in a table layout.
default-table for the list of function calls that are automatically formatted as tables by default. table: Option
,
default-table
Air automatically formats a default set of calls as tables. You can disable these defaults by setting this option to false. The default set currently includes:
tribble()from tribblefcase()from data.table
