Writing R code

Writing R code in Positron should feel quite familiar to RStudio users. However two aspects of the workflow might feel a bit different:

Formatting R code with Air

Positron can format R code with Air, a new R formatter whose creation was largely motivated by Positron.

A formatter takes charge of the layout of your R code, which refers to whitespace, newlines, indentation, and the like. The goal is to enforce a set of style conventions that enhance readability and robustness. Air shares properties with other modern formatters and linters, such as Ruff for Python:

  • It’s highly opinionated and offers relatively few options for customization.
  • It’s written in Rust, which makes it extremely fast.

How do I install Air?

Air is a command line tool which automatically ships with Positron. Most Positron users do not need to do anything intentional to install Air or to keep it up-to-date.

Under the hood, Air is packaged in a VS Code extension, included in Positron as a bootstrapped extension that is installed for you. The Air extension is published on Open VSX, the marketplace used by IDEs like Positron and Cursor, and also on the Visual Studio Code Marketplace, which serves only the proprietary Microsoft builds of Visual Studio Code.

How do I use Air?

There are various explicit and implicit gestures to run Air over your R code:

  • “Format on save”: formats R code every time you save a file. This is a lifestyle you must opt in to, but it is our strong recommendation that you do so. Below we give details on how to set this up.
  • Formatting commands: These are explicit gestures to format specific bits of code.
    • Format Selection
    • Format Document
    • Quarto: Format Cell
    • Air: Format Workspace Folder
    These are all available in the command palette.

All R code receives some basic formatting as you type, but this first pass is focused only on indentation. A second formatting pass with Air, when you save the file or intentionally format it, produces a much better result, since Air works off of an actual syntax tree.

Tip

You can use special comments to tell Air to skip formatting specific sections of code, if you don’t want to apply Air’s strict formatting rules in a specific situation.

Opt in to “Format on save”

The easiest way to get our recommended set up is to run usethis::use_air() (note that this currently requires the development version of usethis). use_air() configures “Format on save” in the current workspace by modifying (or creating, if necessary) the .vscode/settings.json file. It also creates an air.toml file.

VS Code and Positron let you customize settings at the user level and at the workspace level (i.e. for just the current project you are working on). We think “Format on save” makes the most sense to configure at the workspace level:

  • Workspace settings are recorded inside the workspace at .vscode/settings.json, allowing them to be tracked in version control. This means that all collaborators get those settings automatically when they check out the project, enforcing a common formatting style.
  • User level settings apply to any workspaces that you open. This sounds nice in theory, but you might not want automatic Air formatting in older projects or in projects that you don’t own. If you opt in at the user level, you’ll have to turn Air off somehow in these projects or risk creating a huge number of formatting diffs, when that’s probably not your intent.

Here is what .vscode/settings.json might look like for someone who wants their R code formatted on save, in both R scripts and Quarto documents:

{
    "[r]": {
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "Posit.air-vscode"
    },
    "[quarto]": {
        "editor.formatOnSave": true,
        "editor.defaultFormatter": "quarto.quarto"
    }
}

To learn more about using Air, read its documentation for Positron and VS Code.

R snippets

Code snippets let you insert ready-made templates for common coding patterns. For example, for is a reserved word in R that is used to create a loop. In actual usage, for is always part of a larger construct:

for (variable in vector) {
    # code to repeat
}

A snippet for for inserts this whole skeleton, with special placeholders that let you use Tab to move through the positions for variable and vector, and # code to repeat.

Completion list after typing “for”, showing the snippet that can be inserted

It is common for IDEs to provide explicit support for code snippets and this certainly holds true for both RStudio and Positron. This guide is intended to help users who have used snippets in RStudio adapt their approach for Positron, which inherits snippet behavior from its Code OSS architecture.

Documentation on snippets in:

Default and custom snippets in RStudio

RStudio ships with a default set of snippets. For our purposes, it’s useful to break them into two broad classes:

  • Reserved words: These snippets relate to keywords that have special meaning. This includes control flow elements, such as if, while, and for, as well as the function keyword.
  • Everything else: These snippets relate to a selection of functions, mostly in the so-called base packages. Examples include matrix(), the apply() family of functions, and various functions around S4 classes, such as setClass().

You can customize your RStudio snippets via the Edit Snippets button in Global Options > Code. Under the hood, this initializes a user-level snippet file that is pre-populated with RStudio’s default snippets. You can then apply your desired changes, such as adding or deleting snippets. Going forward, this user-level file powers all of your RStudio snippets, i.e. there is no combining of built-in and user-level snippets.

Default and custom snippets in Positron

The completion experience in Positron arises from two components working together:

  • Positron: The IDE controls the overall completion experience, which is the heart of the so-called IntelliSense features in VS Code. IntelliSense provides the user interface for completions and is also in charge of combining/filtering/sorting completions coming from multiple sources, including snippets. These snippets can come from various sources:
    • Extensions
    • User settings
    • Workspace settings
  • R language server: The ark kernel provides an LSP server for R and, in particular, ark produces all (or almost all) of the completions you see for R.

This division of labor means that the completion and snippet responsibilities are a bit different in Positron than in RStudio.

Reserved words

In Positron, the completion items related to R’s reserved words are built into ark. Each reserved word can be completed on its own and, in some cases, as part of a larger snippet.

Completion list with rectangles highlighting two entries for the function reserved word, one as a bare keyword completion and another as a snippet

Everything else

If you want snippets beyond those provided for R’s reserved words, you’ll need to configure that for yourself. Press to open the command palette and type Snippets: Configure Snippets.

Snippets can be stored in three types of files:

  1. “Global”: contains snippets that are not limited to one language. Create with New Global Snippets file….

    • Individual snippets can have a scope which specifies one or more languages. Include "r" in the scope for any snippet you want to use in R.
    • This writes a .code-snippets file alongside your other Positron user settings. It is pre-populated with example snippets for both R and Python.
  2. “Global” but for one workspace: contains snippets for multiple languages but tied to a specific workspace. Create with New Snippets file for ‘MyProject’.

    • This writes a .code-snippets file alongside the other settings for the workspace, pre-populated with example snippets.

    Menu with options for “New Global Snippets file” and “New Snippets file for ‘MyProject’”
  3. Language-specific: contains snippets for one specific language. Create by selecting the language from the drop-down menu, which includes entries for R, Python, and Quarto.

    In the case of R, this writes an r.json file alongside your other Positron user settings. It is pre-populated with an R example snippet.

    Menu with options for creating a new snippet file for Python, Quarto, or R

Although both RStudio and Positron keep track of custom snippets in a file, they don’t use the same syntax. Positron uses the TextMate syntax, which is inherited from VS Code. If you miss specific snippets from RStudio’s defaults, you can consult this file to see what they look like when translated to the syntax used by Positron: r.code-snippets.