From 837a9a816e40780f3fe294590afef8ec92208ed8 Mon Sep 17 00:00:00 2001 From: Companion <3773910+Companion@users.noreply.github.com> Date: Mon, 31 Aug 2026 10:14:44 +0100 Subject: [PATCH] Preserve access modifier separator in Luau array types Closes #1126 --- CHANGELOG.md | 4 ++ src/formatters/luau.rs | 44 ++++++++++++++-- .../type-array-access-modifiers.lua | 40 +++++++++++++++ ..._luau@type-array-access-modifiers.lua.snap | 50 +++++++++++++++++++ 4 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 tests/inputs-luau/type-array-access-modifiers.lua create mode 100644 tests/snapshots/tests__luau@type-array-access-modifiers.lua.snap diff --git a/CHANGELOG.md b/CHANGELOG.md index a10b07c9..213cbe12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Luau: Fixed the `read`/`write` access modifier merging into the element type in array type shorthand, changing the meaning of the code (`{ read Foo }` was formatted as `{ readFoo }`) ([#1126](https://github.com/JohnnyMorganz/StyLua/issues/1126)) + ## [2.5.2] - 2026-05-16 ### Fixed diff --git a/src/formatters/luau.rs b/src/formatters/luau.rs index 60b7ab4a..a86aa9b4 100644 --- a/src/formatters/luau.rs +++ b/src/formatters/luau.rs @@ -293,13 +293,21 @@ fn format_type_info_internal( || contains_comments(access) || contains_comments(type_info); - let access = access.as_ref().map(|token_reference| { - format_token_reference(ctx, token_reference, shape + BRACKET_LEN) - }); + // Format modifier comments at the multiline element indentation. + let access_shape = if contains_comments { + shape.increment_additional_indent() + } else { + shape + BRACKET_LEN + }; + let access = access + .as_ref() + .map(|token_reference| format_token_reference(ctx, token_reference, access_shape)); + + // Count the modifier and its required separator without trivia. let access_shape_increment = access .as_ref() - .map_or(0, |token| token.to_string().len() + 1); + .map_or(0, |token| strip_trivia(token).to_string().len() + 1); let (table_type, new_type_info) = if contains_comments { (TableType::MultiLine, None) @@ -343,10 +351,36 @@ fn format_type_info_internal( ), }; + let (access, type_info_leading_trivia) = match access { + Some(access) => { + // A trailing line comment must end before the element type. + let separator = if access.has_trailing_comments(CommentSearch::Single) { + vec![ + create_newline_trivia(ctx), + create_indent_trivia(ctx, shape.increment_additional_indent()), + ] + } else { + vec![Token::new(TokenType::spaces(1))] + }; + + ( + Some( + access + .update_leading_trivia(leading_trivia) + .update_trailing_trivia(FormatTriviaType::Append(separator)), + ), + FormatTriviaType::NoChange, + ) + } + None => (None, leading_trivia), + }; + TypeInfo::Array { braces, access, - type_info: Box::new(new_type_info.update_trivia(leading_trivia, trailing_trivia)), + type_info: Box::new( + new_type_info.update_trivia(type_info_leading_trivia, trailing_trivia), + ), } } diff --git a/tests/inputs-luau/type-array-access-modifiers.lua b/tests/inputs-luau/type-array-access-modifiers.lua new file mode 100644 index 00000000..281f6c02 --- /dev/null +++ b/tests/inputs-luau/type-array-access-modifiers.lua @@ -0,0 +1,40 @@ +type SingleRead = { read Service } +type SingleWrite = { write Service } + +type MultiRead = { + -- keep this comment + read Service +} + +type MultiWrite = { + -- and keep this one + write Service +} + +type MultiNested = { + read { + Name: string, + Other: number, + } +} + +type CommentBetweenLine = { + read -- between + Service +} + +type CommentBetweenBlock = { read --[[between]] Service } + +type ExcessWhitespace = { read Service } + +type PropertyAccess = { read Name: string } +type IndexerAccess = { read [number]: Service } +type NoAccess = { Service } + +type BoundaryFits = { + field: { read AccessModifierShapeAccountingBoundaryServiceTypeNameFillingTheAvailableColumnBudgetExactlyHereOkX | B }, +} + +type BoundaryHangs = { + field: { read AccessModifierShapeAccountingBoundaryServiceTypeNameFillingTheAvailableColumnBudgetExactlyHereOkXX | B }, +} diff --git a/tests/snapshots/tests__luau@type-array-access-modifiers.lua.snap b/tests/snapshots/tests__luau@type-array-access-modifiers.lua.snap new file mode 100644 index 00000000..baf46cdc --- /dev/null +++ b/tests/snapshots/tests__luau@type-array-access-modifiers.lua.snap @@ -0,0 +1,50 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::Luau)" +input_file: tests/inputs-luau/type-array-access-modifiers.lua +--- +type SingleRead = { read Service } +type SingleWrite = { write Service } + +type MultiRead = { + -- keep this comment + read Service +} + +type MultiWrite = { + -- and keep this one + write Service +} + +type MultiNested = { + read { + Name: string, + Other: number, + } +} + +type CommentBetweenLine = { + read -- between + Service +} + +type CommentBetweenBlock = { + read --[[between]] Service +} + +type ExcessWhitespace = { read Service } + +type PropertyAccess = { read Name: string } +type IndexerAccess = { read [number]: Service } +type NoAccess = { Service } + +type BoundaryFits = { + field: { read AccessModifierShapeAccountingBoundaryServiceTypeNameFillingTheAvailableColumnBudgetExactlyHereOkX | B }, +} + +type BoundaryHangs = { + field: { + read AccessModifierShapeAccountingBoundaryServiceTypeNameFillingTheAvailableColumnBudgetExactlyHereOkXX | B + }, +} +