Skip to content
46 changes: 34 additions & 12 deletions R/rules-indention.R
Original file line number Diff line number Diff line change
Expand Up @@ -35,24 +35,46 @@ unindent_function_declaration <- function(pd, indent_by = 2L) {
#' Is the function declaration single indented?
#'
#' Assumes you already checked if it's a function with
#' `is_function_declaration`. It is single indented if the first token
#' after the first line break that is a `"SYMBOL_FORMALS"`.
#' `is_function_declaration`. "single indented" means the formals are on the
#' line after `function(` and indented one further level, and the closing `)`
#' is also on its own line. The alternative compliant style within the style
#' guide is "hanging indented" where formals share the line with `function(`,
#' any subsequent lines of formals are indented relative to that `(`, and
#' the closing `)` shares a line with the last formal argument.
#' @param pd A parse table.
#' @inheritParams tidyverse_style
#' @keywords internal
is_single_indent_function_declaration <- function(pd, indent_by = 2L) {
head_pd <- vec_slice(pd, -nrow(pd))
line_break_in_header <- which(head_pd$lag_newlines > 0L & head_pd$token == "SYMBOL_FORMALS")
if (length(line_break_in_header) > 0L) {
# indent results from applying the rules, spaces is the initial spaces
# (which is indention if a newline is ahead)
# The 2L factor is kept to convert double indent to single indent
pd$spaces[line_break_in_header[1L] - 1L] <= 2L * indent_by
} else {
FALSE
idx_paren_open <- which(pd$token == "'('")[1L]
idx_paren_close <- which(pd$token == "')'")[1L]

row_idx <- seq_len(nrow(pd))
is_formal <- (
pd$token == "SYMBOL_FORMALS" &
row_idx > idx_paren_open &
row_idx < idx_paren_close
)
if (!any(is_formal)) {
return(FALSE)
}

first_formal_idx <- which(is_formal)[1L]
# If authored with single indentation (first argument on new line),
# return TRUE unless closing parenthesis shares the line (indicating hanging indentation).
if (any(pd$lag_newlines[seq2(idx_paren_open + 1L, first_formal_idx)] > 0L)) {
return(pd$lag_newlines[idx_paren_close] > 0L || pd$spaces[first_formal_idx - 1L] <= 2L * indent_by)
}

# If first argument shares line with '(', check if subsequent arguments on new lines
# have single indentation (<= 4 spaces) vs. hanging indentation (> 4 spaces).
is_formal_nl <- is_formal & pd$lag_newlines > 0L
if (!any(is_formal_nl)) {
return(FALSE)
}
}

first_nl_formal <- which(is_formal_nl)[1L]
pd$spaces[first_nl_formal - 1L] <= 2L * indent_by
}


#' @describeIn update_indention Indents *all* tokens after `token` - including
Expand Down
8 changes: 6 additions & 2 deletions man/is_single_indent_function_declaration.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 47 additions & 0 deletions tests/testthat/fun_dec/line_break_fun_dec-in.R
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,50 @@ a <- function( #
y) {
x - 1
}


# nested multi-line header -> single indentation
list(
a = function(
x,
y) {
x - 1
}
)


# ambiguous case: closing parenthesis starts on new line -> single indentation
a <- function(
x,
y
) {
x - 1
}


# ambiguous case: closing parenthesis starts on new line -> single indentation
a <- function(
x,
y
) {
x - 1
}


# mixed structure A: subsequent formal on new line (<= 4 spaces) -> single indentation
f <- function(a =
1,
b = 2
) {}


# mixed structure B: heavily indented (> 4 spaces) AND ')' on same line -> hanging indentation
f <- function(a = 1,
b = 2) {}


# mixed structure C: heavily indented (> 4 spaces) BUT ')' starts on new line -> single indentation
f <- function(a =
1,
b = 2
) {}
48 changes: 48 additions & 0 deletions tests/testthat/fun_dec/line_break_fun_dec-out.R
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,51 @@ a <- function(
) {
x - 1
}


# nested multi-line header -> single indentation
list(
a = function(
x,
y
) {
x - 1
}
)


# ambiguous case: closing parenthesis starts on new line -> single indentation
a <- function(
x,
y
) {
x - 1
}


# ambiguous case: closing parenthesis starts on new line -> single indentation
a <- function(
x,
y
) {
x - 1
}


# mixed structure A: subsequent formal on new line (<= 4 spaces) -> single indentation
f <- function(
a =
1,
b = 2
) {}


# mixed structure B: heavily indented (> 4 spaces) AND ')' on same line -> hanging indentation
f <- function(a = 1,
b = 2) {}


# mixed structure C: heavily indented (> 4 spaces) BUT ')' starts on new line -> single indentation
f <- function(a =
1,
b = 2) {}
Loading