Skip to content

Fix assignment width check for multiline variable lists - #1187

Open
d-enk wants to merge 1 commit into
JohnnyMorganz:mainfrom
d-enk:fix/assignment-multiline-var-list-width
Open

d-enk wants to merge 1 commit into
JohnnyMorganz:mainfrom
d-enk:fix/assignment-multiline-var-list-width

Conversation

@d-enk

@d-enk d-enk commented Nov 3, 2026

Copy link
Copy Markdown

Problem

The check deciding whether an assignment fits the column width measured the variable list by its total length. When the list itself spans multiple lines, the total length is far larger than what actually sits on the line with the equals token, so the assigned function's parameters were expanded unnecessarily.

Given (default column_width = 120)

handler.group("spec", {
	{ name = "alpha", enabled = false },
	{ name = "beta", enabled = true },
	{ name = "gamma", enabled = true },
}).setup = function(context)
	return context
end

StyLua produced

handler.group("spec", {
	{ name = "alpha", enabled = false },
	{ name = "beta", enabled = true },
	{ name = "gamma", enabled = true },
}).setup = function(
	context
)
	return context
end

Only }).setup (8 characters) shares a line with the equals token, but the whole variable list (143 characters) was added to the width, pushing the check past 120. Whether this triggers therefore depends on how many rows the table happens to have, not on the width of the line that is actually produced.

The same measurement feeds format_local_assignment_no_trivia and format_const_assignment_no_trivia, where Luau type specifiers and Lua 5.4 attributes are also appended to the variable list:

local values: {
	field_01: string,
	field_02: string,
	field_03: string,
	field_04: string,
	field_05: string,
	field_06: string,
} = function(context)
	return context
end

The type specifier is 125 characters across 8 lines, but only } sits on the line with the equals token, so the parameters were expanded at the default column_width = 120.

Fix

Measure the last line of the variable list, which is what the equals token and the expression are appended to:

let singleline_shape = shape.take_last_line(&strip_leading_trivia(&var_list))
    + 3 // 3 = " = "
    + strip_trailing_trivia(&expr_list).to_string().len();

For local and const, the type specifier and attribute text is appended to the variable list first, so a multiline specifier only contributes its last line. The prefix keyword (local / const ) is added before taking the last line, so it is dropped when the list is multiline and kept when it is not.

This is the same approach the TableConstructor branch of function_args_multiline_heuristic uses for the equivalent width check.

Tests

Six tests added in tests/tests.rs:

Test Covers
test_assignment_var_list_spanning_multiple_lines format_assignment_no_trivia
test_assignment_var_list_spanning_multiple_lines_still_expands_when_over_budget guard: parameters genuinely do not fit, so they must still expand
test_local_assignment_name_list_spanning_multiple_lines format_local_assignment_no_trivia — name list split by a comment
test_const_assignment_name_list_spanning_multiple_lines format_const_assignment_no_trivia — same, for Luau const
test_local_assignment_multiline_type_specifier Luau: multiline type specifier
test_const_assignment_multiline_type_specifier Luau: same, for const

Each of the five regression tests fails when the fix is reverted, so none of them passes by accident. The guard test passes both with and without the fix, which is the point: it checks that the parameters are still expanded when they genuinely do not fit. The Luau tests are gated behind #[cfg(feature = "luau")].

Verification

cargo test passes for every feature set in ci.yml (default, --no-default-features, luau, lua52, lua53, lua54, luajit, cfxlua, --all-features), along with cargo clippy --all-features --all-targets -- -D warnings and cargo fmt --check. No existing snapshots change.

The Docker image builds and formats the example above correctly. The wasm check was not run locally (no wasm32-unknown-unknown target available), and the large-scale comparison workflow is workflow_dispatch-only and requires the PR number, so both are left to CI.

The check deciding whether an assignment fits the column width measured the
variable list by its total length. When the list itself spans multiple lines,
the total length is far larger than what actually sits on the line with the
equals token, so the assigned function's parameters were expanded
unnecessarily. The same applied to Luau type specifiers and Lua 5.4
attributes, which are also appended to the variable list before the equals
token.

Measure the last line of the variable list instead, which is what the equals
token and the expression are appended to:

    let singleline_shape = shape.take_last_line(&strip_leading_trivia(&var_list))
        + 3 // 3 = " = "
        + strip_trailing_trivia(&expr_list).to_string().len();

This is the same approach the TableConstructor branch of
function_args_multiline_heuristic uses for the equivalent width check.

Adds regression tests for each affected path, plus a guard test asserting
that the parameters are still expanded when they genuinely do not fit.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant