Skip to content

Formatter

Documentation for Roughly's R code formatter.

Roughly includes a non-invasive R code formatter that emphasizes readability while respecting the existing structure of your code.

Usage

Format your R files using the command line:

Terminal window
roughly fmt # Format all files in the current directory
roughly fmt <path> # Format all files in <path>
roughly fmt --check # Only check if files would be formatted
roughly fmt --diff # Show diff of formatting changes without applying them

Philosophy

The formatter follows these key principles:

  • Non-invasive formatting: The formatter only adds line breaks if expressions are already multi-line, and won’t break one-liners unnecessarily
  • Consistent style: Standardizes spacing, indentation, and other aspects of R code style

Formatting Rules

Roughly applies specific formatting rules to different R code constructs. Here are the detailed rules with before/after examples:

Binary Operators

Spaces are added around binary operators, except for the range operator (:):

# Before formatting
x<-1
1+2
1:10
# After formatting
x <- 1
1 + 2
1:10

Multi-line expressions with binary operators maintain indentation for readability:

# Before formatting
foo() %>%
bar() %>%
baz()
# After formatting
foo() %>%
bar() %>%
baz()

Blocks

Code blocks are consistently indented. Single-line blocks remain compact (even with semicolons), while multi-line blocks format each expression on its own line:

# Before formatting
{foo;bar}
{
foo; bar
}
# After formatting
{ foo; bar }
{
foo
bar
}

Comments

Comments are formatted by adding a space after the hash if necessary:

# Before formatting
#foo
#'foo
# After formatting
# foo
#' foo

The formatter preserves:

  • Roxygen comments (#')
  • Special comment types (e.g., ##, ###, #!/usr/bin/env Rscript)

Empty Lines

Only one empty line is allowed between code blocks. Successive newlines are merged:

# Before formatting
function() {
foo()
bar()
}
# After formatting
function() {
foo()
bar()
}

Function Calls

Arguments are formatted with consistent spacing around equals signs. Single-line argument lists remain on one line, while multi-line arguments receive proper indentation:

# Before formatting
foo(a=1,b=2)
bar(a=1,
b=2)
# After formatting
foo(a = 1, b = 2)
bar(
a = 1,
b = 2
)

Function Definitions

Function definitions follow consistent formatting rules for parameters and body:

# Before formatting
foo <- function(a=1, b=2) {}
bar <- function(a=1,
b=2) {}
# After formatting
foo <- function(a = 1, b = 2) {}
bar <- function(
a = 1,
b = 2
) {}

If Statements

One-line conditional statements are preserved, but multi-line bodies trigger consistent formatting for all parts:

# Before formatting
if (x) {y} else {z}
if (x) {
y
} else {z}
# After formatting
if (x) { y } else { z }
if (x) {
y
} else {
z
}

For if-statements with multi-line conditions, the formatter enforces a multiline block structure for the body:

# Before formatting
if (
x && y
) z
# After formatting
if (
x && y
) {
z
}

Loops

For for, while, and repeat loops, a block is always enforced for the body:

# Before formatting
for (i in 1:3) foo()
while (TRUE) foo()
repeat foo()
# After formatting
for (i in 1:3) {
foo()
}
while (TRUE) {
foo()
}
repeat {
foo()
}

Parenthesized Expressions

Parenthesized expressions maintain their layout, with consistent formatting for multi-line expressions:

# Before formatting
(a+b)
(a +
b)
# After formatting
(a + b)
(
a + b
)

Strings

String literals are consistently formatted, preserving escape sequences and multi-line content:

# Before formatting
x <- "hello"
y <- 'world'
# After formatting
x <- "hello"
y <- "world"

Subset Operations

Subset operations ([] and [[]]) follow the same formatting rules as function calls:

# Before formatting
x[i=1,j=2]
x[i=1,
j=2]
# After formatting
x[i = 1, j = 2]
x[
i = 1,
j = 2
]

Format Suppression

You can disable formatting for specific code sections using the # fmt: skip comment directive:

# fmt: skip
matrix(
c(
1, 2,
3, 4
),
nrow=2
) # This code won't be reformatted
matrix(c(1, 2,
3, 4), nrow = 2) # This code will be formatted
matrix(c(1, 2,
3, 4), nrow=2) # fmt: skip
# The line above won't be reformatted

The fmt: skip directive can be placed:

  • Before a specific line to skip formatting that line
  • At the end of a line to skip formatting that line

Special Cases

The formatter intelligently handles various R idioms and special patterns:

  • Blocks in calls or parenthesized expressions: When a single code block is inside parentheses, it doesn’t receive an additional level of indentation (e.g. foo({ expr }))
  • Empty blocks: Consistently formats empty blocks ({})
  • Empty lines in R6 definitions: Allows one empty line in R6 class definitions
  • Expression sequences: Maintains readability in sequences with semicolons (e.g., { expr1; expr2 })
  • Matrix indexing: Handles complex subsetting with empty dimensions ([,,])
  • Multi-line strings: Preserves structure in multi-line string literals
  • Special comments: Respects shebangs, roxygen documentation, and other comment types
  • Switch statements: Properly formats fallthrough cases (case = ,)

Line Endings

The formatter automatically detects and preserves the line ending style (LF or CRLF) used in the original file.