diff --git a/inst/tinytest/test_rformat.R b/inst/tinytest/test_rformat.R index d46718d..b097e44 100644 --- a/inst/tinytest/test_rformat.R +++ b/inst/tinytest/test_rformat.R @@ -50,6 +50,20 @@ expect_true( info = "Inline function body should be preserved" ) +# Multi-line function header + bare if body + expand_if (regression) +# The signature collapse used to leave blank lines between function(...) +# and the body, making it look like two separate statements. +code <- "`%||%` <- function(\n x,\n y\n) if (is.null(x)) y else x" +result <- rformat(code, control_braces = "multi", expand_if = TRUE) +expect_true( + !is.null(tryCatch(parse(text = result), error = function(e) NULL)), + info = "Collapsed signature with bare if body should still parse" +) +expect_false( + grepl("function\\(x, y\\)\\s*\\n\\s*\\n", result), + info = "No blank line should separate collapsed signature from its bare body" +) + # Nested parentheses collapse (regression test) # Short nested calls should collapse to a single line code <- "tryCatch( diff --git a/src/funcdef.cpp b/src/funcdef.cpp index a244977..aa4f0fa 100644 --- a/src/funcdef.cpp +++ b/src/funcdef.cpp @@ -134,6 +134,19 @@ void reformat_function_defs(std::vector& tokens, need_change = true; if (!need_change) continue; + // Record the last line the signature currently occupies so we + // can close any gap between the collapsed signature and a + // bare (braceless) body. + int old_sig_end_line = tokens[fi].out_line; + for (int k = fi; k <= close_idx; k++) { + if (tokens[k].out_line > old_sig_end_line) + old_sig_end_line = tokens[k].out_line; + } + for (int c : comma_indices) { + if (tokens[c].out_line > old_sig_end_line) + old_sig_end_line = tokens[c].out_line; + } + for (int k = fi; k <= close_idx; k++) tokens[k].out_line = func_line; for (int c : comma_indices) @@ -141,6 +154,22 @@ void reformat_function_defs(std::vector& tokens, if (has_brace) tokens[brace_idx].out_line = func_line; + // Close the gap for a bare-body function: shift any tokens + // that lived on the old signature's trailing lines up so the + // body stays adjacent to the collapsed signature. + if (!has_brace && old_sig_end_line > func_line) { + int shift = old_sig_end_line - func_line; + for (int k = 0; k < n; k++) { + if (tokens[k].out_line >= old_sig_end_line + 1) { + tokens[k].out_line -= shift; + } else if (tokens[k].out_line == old_sig_end_line) { + // Body tokens that shared the old close-paren + // line now belong on func_line. + tokens[k].out_line = func_line; + } + } + } + reorder_tokens(tokens); changed = true; break;