From 2356e3f6e906e14bf57563d35a1c788a72d96c54 Mon Sep 17 00:00:00 2001 From: Kuuzoo Date: Mon, 10 Feb 2025 02:26:11 +0100 Subject: [PATCH 01/16] start with cfxlua support --- Cargo.toml | 1 + src/cli/opt.rs | 1 + src/formatters/general.rs | 19 +++++++++++++++---- src/lib.rs | 5 +++++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e3c19140..415c714b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ lua52 = ["full_moon/lua52"] lua53 = ["lua52", "full_moon/lua53"] lua54 = ["lua53", "full_moon/lua54"] luajit = ["full_moon/luajit"] +cfxlua = ["lua54", "full_moon/cfxlua"] editorconfig = ["ec4rs"] [dependencies] diff --git a/src/cli/opt.rs b/src/cli/opt.rs index 3769317f..d982fc36 100644 --- a/src/cli/opt.rs +++ b/src/cli/opt.rs @@ -237,6 +237,7 @@ convert_enum!(LuaVersion, ArgLuaVersion, { #[cfg(feature = "lua54")] Lua54, #[cfg(feature = "luau")] Luau, #[cfg(feature = "luajit")] LuaJIT, + #[cfg(feature = "cfxlua")] CFXLua, }); convert_enum!(LineEndings, ArgLineEndings, { diff --git a/src/formatters/general.rs b/src/formatters/general.rs index 16cc8fc6..ce26e06b 100644 --- a/src/formatters/general.rs +++ b/src/formatters/general.rs @@ -186,10 +186,21 @@ pub fn format_token( } }) .into(); - TokenType::StringLiteral { - literal, - multi_line_depth: *multi_line_depth, - quote_type: quote_to_use, + // if we are using CFXLua and the quote type is backtick, then we need stick with backticks + #[cfg(feature = "cfxlua")] + use crate::LuaVersion; + if ctx.config().syntax == LuaVersion::CFXLua && matches!(quote_type, StringLiteralQuoteType::Backtick) { + TokenType::StringLiteral { + literal, + multi_line_depth: *multi_line_depth, + quote_type: StringLiteralQuoteType::Backtick, + } + } else { + TokenType::StringLiteral { + literal, + multi_line_depth: *multi_line_depth, + quote_type: quote_to_use, + } } } } diff --git a/src/lib.rs b/src/lib.rs index 4a25c8e2..a6cc4fef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,6 +42,9 @@ pub enum LuaVersion { /// Parse LuaJIT code #[cfg(feature = "luajit")] LuaJIT, + /// Parse CFX Lua code + #[cfg(feature = "cfxlua")] + CFXLua, } impl From for full_moon::LuaVersion { @@ -59,6 +62,8 @@ impl From for full_moon::LuaVersion { LuaVersion::Luau => full_moon::LuaVersion::luau(), #[cfg(feature = "luajit")] LuaVersion::LuaJIT => full_moon::LuaVersion::luajit(), + #[cfg(feature = "cfxlua")] + LuaVersion::CFXLua => full_moon::LuaVersion::cfxlua(), } } } From dbb6a2234e6dbe02df00e749118daeb20a42f0b2 Mon Sep 17 00:00:00 2001 From: Kuuzoo Date: Mon, 21 Apr 2025 04:12:38 +0200 Subject: [PATCH 02/16] fix: enhance CFXLua support for backtick string literals in format_token function --- src/formatters/general.rs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/formatters/general.rs b/src/formatters/general.rs index ce26e06b..18b3f161 100644 --- a/src/formatters/general.rs +++ b/src/formatters/general.rs @@ -186,16 +186,30 @@ pub fn format_token( } }) .into(); - // if we are using CFXLua and the quote type is backtick, then we need stick with backticks + + // If the syntax is CFXLua and the quote type is backtick, retain the use of backticks since those have a runtime functionality in CFXLua #[cfg(feature = "cfxlua")] - use crate::LuaVersion; - if ctx.config().syntax == LuaVersion::CFXLua && matches!(quote_type, StringLiteralQuoteType::Backtick) { - TokenType::StringLiteral { - literal, - multi_line_depth: *multi_line_depth, - quote_type: StringLiteralQuoteType::Backtick, + { + use crate::LuaVersion; + if ctx.config().syntax == LuaVersion::CFXLua + && matches!(quote_type, StringLiteralQuoteType::Backtick) + { + TokenType::StringLiteral { + literal, + multi_line_depth: *multi_line_depth, + quote_type: StringLiteralQuoteType::Backtick, + } + } else { + TokenType::StringLiteral { + literal, + multi_line_depth: *multi_line_depth, + quote_type: quote_to_use, + } } - } else { + } + + #[cfg(not(feature = "cfxlua"))] + { TokenType::StringLiteral { literal, multi_line_depth: *multi_line_depth, From f4b02815f64bd07338306668f4c2d441d6a3a23d Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 16:02:50 +0200 Subject: [PATCH 03/16] CFXLua -> CfxLua --- src/cli/opt.rs | 2 +- src/formatters/general.rs | 2 +- src/lib.rs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cli/opt.rs b/src/cli/opt.rs index d982fc36..2e848160 100644 --- a/src/cli/opt.rs +++ b/src/cli/opt.rs @@ -237,7 +237,7 @@ convert_enum!(LuaVersion, ArgLuaVersion, { #[cfg(feature = "lua54")] Lua54, #[cfg(feature = "luau")] Luau, #[cfg(feature = "luajit")] LuaJIT, - #[cfg(feature = "cfxlua")] CFXLua, + #[cfg(feature = "cfxlua")] CfxLua, }); convert_enum!(LineEndings, ArgLineEndings, { diff --git a/src/formatters/general.rs b/src/formatters/general.rs index 18b3f161..e83ed0b8 100644 --- a/src/formatters/general.rs +++ b/src/formatters/general.rs @@ -191,7 +191,7 @@ pub fn format_token( #[cfg(feature = "cfxlua")] { use crate::LuaVersion; - if ctx.config().syntax == LuaVersion::CFXLua + if ctx.config().syntax == LuaVersion::CfxLua && matches!(quote_type, StringLiteralQuoteType::Backtick) { TokenType::StringLiteral { diff --git a/src/lib.rs b/src/lib.rs index a6cc4fef..a6736a86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,9 +42,9 @@ pub enum LuaVersion { /// Parse LuaJIT code #[cfg(feature = "luajit")] LuaJIT, - /// Parse CFX Lua code + /// Parse Cfx Lua code #[cfg(feature = "cfxlua")] - CFXLua, + CfxLua, } impl From for full_moon::LuaVersion { @@ -63,7 +63,7 @@ impl From for full_moon::LuaVersion { #[cfg(feature = "luajit")] LuaVersion::LuaJIT => full_moon::LuaVersion::luajit(), #[cfg(feature = "cfxlua")] - LuaVersion::CFXLua => full_moon::LuaVersion::cfxlua(), + LuaVersion::CfxLua => full_moon::LuaVersion::cfxlua(), } } } From 8f6a3060926c13b7974b6be13caf9e8f2ab74c27 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 16:08:01 +0200 Subject: [PATCH 04/16] Add tests --- tests/inputs-cfxlua/addition_assignment_operator.lua | 3 +++ tests/inputs-cfxlua/ampersand_equal.lua | 2 ++ .../inputs-cfxlua/bitwise_xor_assignment_operator.lua | 2 ++ tests/inputs-cfxlua/c_style_comments.lua | 6 ++++++ tests/inputs-cfxlua/compile_time_jenkins_hashes.lua | 1 + tests/inputs-cfxlua/division_assignment_operator.lua | 2 ++ tests/inputs-cfxlua/double_greater_than_equal.lua | 2 ++ tests/inputs-cfxlua/double_less_than_equal.lua | 2 ++ tests/inputs-cfxlua/each_iteration.lua | 2 ++ tests/inputs-cfxlua/in_unpacking.lua | 7 +++++++ .../multiplication_assignment_operator.lua | 2 ++ tests/inputs-cfxlua/pipe_equal.lua | 2 ++ tests/inputs-cfxlua/question_mark_dot.lua | 10 ++++++++++ tests/inputs-cfxlua/set_constructor.lua | 1 + .../inputs-cfxlua/subtraction_assignment_operator.lua | 2 ++ tests/tests.rs | 9 +++++++++ 16 files changed, 55 insertions(+) create mode 100644 tests/inputs-cfxlua/addition_assignment_operator.lua create mode 100644 tests/inputs-cfxlua/ampersand_equal.lua create mode 100644 tests/inputs-cfxlua/bitwise_xor_assignment_operator.lua create mode 100644 tests/inputs-cfxlua/c_style_comments.lua create mode 100644 tests/inputs-cfxlua/compile_time_jenkins_hashes.lua create mode 100644 tests/inputs-cfxlua/division_assignment_operator.lua create mode 100644 tests/inputs-cfxlua/double_greater_than_equal.lua create mode 100644 tests/inputs-cfxlua/double_less_than_equal.lua create mode 100644 tests/inputs-cfxlua/each_iteration.lua create mode 100644 tests/inputs-cfxlua/in_unpacking.lua create mode 100644 tests/inputs-cfxlua/multiplication_assignment_operator.lua create mode 100644 tests/inputs-cfxlua/pipe_equal.lua create mode 100644 tests/inputs-cfxlua/question_mark_dot.lua create mode 100644 tests/inputs-cfxlua/set_constructor.lua create mode 100644 tests/inputs-cfxlua/subtraction_assignment_operator.lua diff --git a/tests/inputs-cfxlua/addition_assignment_operator.lua b/tests/inputs-cfxlua/addition_assignment_operator.lua new file mode 100644 index 00000000..a2e33e5e --- /dev/null +++ b/tests/inputs-cfxlua/addition_assignment_operator.lua @@ -0,0 +1,3 @@ +local index = 1 + +index += 1 \ No newline at end of file diff --git a/tests/inputs-cfxlua/ampersand_equal.lua b/tests/inputs-cfxlua/ampersand_equal.lua new file mode 100644 index 00000000..476520ae --- /dev/null +++ b/tests/inputs-cfxlua/ampersand_equal.lua @@ -0,0 +1,2 @@ +local num = 6 +num &= 3 \ No newline at end of file diff --git a/tests/inputs-cfxlua/bitwise_xor_assignment_operator.lua b/tests/inputs-cfxlua/bitwise_xor_assignment_operator.lua new file mode 100644 index 00000000..4b916461 --- /dev/null +++ b/tests/inputs-cfxlua/bitwise_xor_assignment_operator.lua @@ -0,0 +1,2 @@ +local num = 5 +num ^= 3 \ No newline at end of file diff --git a/tests/inputs-cfxlua/c_style_comments.lua b/tests/inputs-cfxlua/c_style_comments.lua new file mode 100644 index 00000000..82d8fc4c --- /dev/null +++ b/tests/inputs-cfxlua/c_style_comments.lua @@ -0,0 +1,6 @@ +print("Hello, World!") /* Comment */ +/* +Multi +Line +Comment +*/ \ No newline at end of file diff --git a/tests/inputs-cfxlua/compile_time_jenkins_hashes.lua b/tests/inputs-cfxlua/compile_time_jenkins_hashes.lua new file mode 100644 index 00000000..56b5ce59 --- /dev/null +++ b/tests/inputs-cfxlua/compile_time_jenkins_hashes.lua @@ -0,0 +1 @@ +print(`Hello, World!`) diff --git a/tests/inputs-cfxlua/division_assignment_operator.lua b/tests/inputs-cfxlua/division_assignment_operator.lua new file mode 100644 index 00000000..f34cd0cd --- /dev/null +++ b/tests/inputs-cfxlua/division_assignment_operator.lua @@ -0,0 +1,2 @@ +local num = 10 +num /= 2 \ No newline at end of file diff --git a/tests/inputs-cfxlua/double_greater_than_equal.lua b/tests/inputs-cfxlua/double_greater_than_equal.lua new file mode 100644 index 00000000..a77bb4c6 --- /dev/null +++ b/tests/inputs-cfxlua/double_greater_than_equal.lua @@ -0,0 +1,2 @@ +local num = 4 +num >>= 2 \ No newline at end of file diff --git a/tests/inputs-cfxlua/double_less_than_equal.lua b/tests/inputs-cfxlua/double_less_than_equal.lua new file mode 100644 index 00000000..076f12b4 --- /dev/null +++ b/tests/inputs-cfxlua/double_less_than_equal.lua @@ -0,0 +1,2 @@ +local num = 1 +num <<= 2 diff --git a/tests/inputs-cfxlua/each_iteration.lua b/tests/inputs-cfxlua/each_iteration.lua new file mode 100644 index 00000000..1e0b6fde --- /dev/null +++ b/tests/inputs-cfxlua/each_iteration.lua @@ -0,0 +1,2 @@ +local t = { 1, 2, 3 } +for k, v in each(t) do print(k, v) end diff --git a/tests/inputs-cfxlua/in_unpacking.lua b/tests/inputs-cfxlua/in_unpacking.lua new file mode 100644 index 00000000..e9672ced --- /dev/null +++ b/tests/inputs-cfxlua/in_unpacking.lua @@ -0,0 +1,7 @@ +local t = { + a = 1, + b = 2, + c = 3 +} + +local a, b, c in t \ No newline at end of file diff --git a/tests/inputs-cfxlua/multiplication_assignment_operator.lua b/tests/inputs-cfxlua/multiplication_assignment_operator.lua new file mode 100644 index 00000000..c7038711 --- /dev/null +++ b/tests/inputs-cfxlua/multiplication_assignment_operator.lua @@ -0,0 +1,2 @@ +local base = 10 +base *= 2 diff --git a/tests/inputs-cfxlua/pipe_equal.lua b/tests/inputs-cfxlua/pipe_equal.lua new file mode 100644 index 00000000..97658a7c --- /dev/null +++ b/tests/inputs-cfxlua/pipe_equal.lua @@ -0,0 +1,2 @@ +local num = 4 +num |= 1 diff --git a/tests/inputs-cfxlua/question_mark_dot.lua b/tests/inputs-cfxlua/question_mark_dot.lua new file mode 100644 index 00000000..b0ad41ad --- /dev/null +++ b/tests/inputs-cfxlua/question_mark_dot.lua @@ -0,0 +1,10 @@ +local a = { + b = 1, + c = 2, + d = { + e = 3 + } +} + +local x = a?.b +local y = a?.d?.e diff --git a/tests/inputs-cfxlua/set_constructor.lua b/tests/inputs-cfxlua/set_constructor.lua new file mode 100644 index 00000000..64b5469f --- /dev/null +++ b/tests/inputs-cfxlua/set_constructor.lua @@ -0,0 +1 @@ +t = { .a, b = false } diff --git a/tests/inputs-cfxlua/subtraction_assignment_operator.lua b/tests/inputs-cfxlua/subtraction_assignment_operator.lua new file mode 100644 index 00000000..21bb55d5 --- /dev/null +++ b/tests/inputs-cfxlua/subtraction_assignment_operator.lua @@ -0,0 +1,2 @@ +local ten = 10 +ten -= 5 diff --git a/tests/tests.rs b/tests/tests.rs index c5a49b43..dd2ff45e 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -73,6 +73,15 @@ fn test_lua54() { }) } +#[test] +#[cfg(feature = "cfxlua")] +fn test_cfxlua() { + insta::glob!("inputs-cfxlua/*.lua", |path| { + let contents = std::fs::read_to_string(path).unwrap(); + insta::assert_snapshot!(format(&contents, LuaVersion::CfxLua)); + }) +} + #[test] fn test_ignores() { insta::glob!("inputs-ignore/*.lua", |path| { From 393a85b06c32678989f1234ff191b76841083883 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 16:14:42 +0200 Subject: [PATCH 05/16] Add CfxLua to CI --- .github/workflows/ci.yml | 9 ++++++++- .github/workflows/release.yml | 2 +- .github/workflows/test-cases.yml | 1 + wasm/build-wasm.sh | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b28bb79..86fa9847 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,6 +56,13 @@ jobs: - name: Test (LuaJIT) run: cargo test --features luajit + test_cfxlua: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Test (CfxLua) + run: cargo test --features cfxlua + test_all_features: runs-on: ubuntu-latest steps: @@ -70,7 +77,7 @@ jobs: - name: Test Build (wasm) run: | rustup target add wasm32-unknown-unknown - cargo check --target wasm32-unknown-unknown --features luau,lua52,lua53,lua54,luajit + cargo check --target wasm32-unknown-unknown --features luau,lua52,lua53,lua54,luajit,cfxlua test_wasm_build: runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 949ecff7..cd197f04 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,7 +64,7 @@ jobs: sudo apt install ${{ matrix.linker }} - name: Build Binary (All features) - run: cargo build --verbose --locked --release --features lua52,lua53,lua54,luau,luajit --target ${{ matrix.cargo-target }} + run: cargo build --verbose --locked --release --features lua52,lua53,lua54,luau,luajit,cfxlua --target ${{ matrix.cargo-target }} env: CARGO_TARGET_DIR: output diff --git a/.github/workflows/test-cases.yml b/.github/workflows/test-cases.yml index 22549293..4b5524fe 100644 --- a/.github/workflows/test-cases.yml +++ b/.github/workflows/test-cases.yml @@ -21,6 +21,7 @@ jobs: CI=false cargo insta test --features lua53 --accept CI=false cargo insta test --features lua54 --accept CI=false cargo insta test --features luau --accept + CI=false cargo insta test --features cfxlua --accept - name: Create Pull Request uses: peter-evans/create-pull-request@v5 diff --git a/wasm/build-wasm.sh b/wasm/build-wasm.sh index 87259bc2..08c630dd 100755 --- a/wasm/build-wasm.sh +++ b/wasm/build-wasm.sh @@ -1,7 +1,7 @@ # TODO: Ensure that version is up to date cp README.md wasm/ cp LICENSE.md wasm/ -npx wasm-pack@0.10.3 build --target web --out-dir wasm/stylua.web -- --features lua52,lua53,lua54,luajit,luau +npx wasm-pack@0.10.3 build --target web --out-dir wasm/stylua.web -- --features lua52,lua53,lua54,luajit,luau,cfxlua # workaround for bundler usage echo "export { getImports as __getImports, finalizeInit as __finalizeInit }" >> wasm/stylua.web/stylua_lib.js From b9ffb64bc3e478f356dc42fd2ba2dfa0522104c9 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 16:25:33 +0200 Subject: [PATCH 06/16] Move compound op formatting to generic location --- src/formatters/compound_assignment.rs | 60 +++++++++++++++++++++++++++ src/formatters/luau.rs | 39 +---------------- src/formatters/mod.rs | 2 + src/formatters/stmt.rs | 2 + 4 files changed, 66 insertions(+), 37 deletions(-) create mode 100644 src/formatters/compound_assignment.rs diff --git a/src/formatters/compound_assignment.rs b/src/formatters/compound_assignment.rs new file mode 100644 index 00000000..d0542a12 --- /dev/null +++ b/src/formatters/compound_assignment.rs @@ -0,0 +1,60 @@ +use crate::{ + context::{create_indent_trivia, create_newline_trivia, Context}, + fmt_op, fmt_symbol, + formatters::{ + expression::{format_expression, format_var}, + trivia::{ + strip_leading_trivia, FormatTriviaType, UpdateLeadingTrivia, UpdateTrailingTrivia, + }, + }, + shape::Shape, +}; +use full_moon::{ + ast::{CompoundAssignment, CompoundOp}, + tokenizer::TokenReference, +}; + +pub fn format_compound_op(ctx: &Context, compound_op: &CompoundOp, shape: Shape) -> CompoundOp { + fmt_op!(ctx, CompoundOp, compound_op, shape, { + PlusEqual = " += ", + MinusEqual = " -= ", + StarEqual = " *= ", + SlashEqual = " /= ", + #[cfg(feature = "luau")] + PercentEqual = " %= ", + CaretEqual = " ^= ", + #[cfg(feature = "luau")] + TwoDotsEqual = " ..= ", + #[cfg(feature = "luau")] + DoubleSlashEqual = " //= ", + #[cfg(feature = "cfxlua")] + DoubleLessThanEqual = " <<= ", + #[cfg(feature = "cfxlua")] + DoubleGreaterThanEqual = " >>= ", + #[cfg(feature = "cfxlua")] + AmpersandEqual = " &= ", + #[cfg(feature = "cfxlua")] + PipeEqual = " |= ", + }, |other| panic!("unknown node {:?}", other)) +} + +pub fn format_compound_assignment( + ctx: &Context, + compound_assignment: &CompoundAssignment, + shape: Shape, +) -> CompoundAssignment { + // Calculate trivia + let leading_trivia = vec![create_indent_trivia(ctx, shape)]; + let trailing_trivia = vec![create_newline_trivia(ctx)]; + + let lhs = format_var(ctx, compound_assignment.lhs(), shape) + .update_leading_trivia(FormatTriviaType::Append(leading_trivia)); + let compound_operator = format_compound_op(ctx, compound_assignment.compound_operator(), shape); + let shape = shape + + (strip_leading_trivia(&lhs).to_string().len() + compound_operator.to_string().len()); + + let rhs = format_expression(ctx, compound_assignment.rhs(), shape) + .update_trailing_trivia(FormatTriviaType::Append(trailing_trivia)); + + CompoundAssignment::new(lhs, compound_operator, rhs) +} diff --git a/src/formatters/luau.rs b/src/formatters/luau.rs index d1ec5327..dc56f133 100644 --- a/src/formatters/luau.rs +++ b/src/formatters/luau.rs @@ -2,10 +2,10 @@ use crate::{ context::{ create_function_definition_trivia, create_indent_trivia, create_newline_trivia, Context, }, - fmt_op, fmt_symbol, + fmt_symbol, formatters::{ assignment::hang_equal_token, - expression::{format_expression, format_var}, + expression::format_expression, functions::format_function_body, general::{ format_contained_punctuated_multiline, format_contained_span, format_punctuated, @@ -32,46 +32,11 @@ use full_moon::ast::{ TypeInfo, TypeIntersection, TypeSpecifier, TypeUnion, }, punctuated::Pair, - CompoundAssignment, CompoundOp, }; use full_moon::ast::{punctuated::Punctuated, span::ContainedSpan}; use full_moon::tokenizer::{Token, TokenReference, TokenType}; use std::boxed::Box; -pub fn format_compound_op(ctx: &Context, compound_op: &CompoundOp, shape: Shape) -> CompoundOp { - fmt_op!(ctx, CompoundOp, compound_op, shape, { - PlusEqual = " += ", - MinusEqual = " -= ", - StarEqual = " *= ", - SlashEqual = " /= ", - PercentEqual = " %= ", - CaretEqual = " ^= ", - TwoDotsEqual = " ..= ", - DoubleSlashEqual = " //= ", - }, |other| panic!("unknown node {:?}", other)) -} - -pub fn format_compound_assignment( - ctx: &Context, - compound_assignment: &CompoundAssignment, - shape: Shape, -) -> CompoundAssignment { - // Calculate trivia - let leading_trivia = vec![create_indent_trivia(ctx, shape)]; - let trailing_trivia = vec![create_newline_trivia(ctx)]; - - let lhs = format_var(ctx, compound_assignment.lhs(), shape) - .update_leading_trivia(FormatTriviaType::Append(leading_trivia)); - let compound_operator = format_compound_op(ctx, compound_assignment.compound_operator(), shape); - let shape = shape - + (strip_leading_trivia(&lhs).to_string().len() + compound_operator.to_string().len()); - - let rhs = format_expression(ctx, compound_assignment.rhs(), shape) - .update_trailing_trivia(FormatTriviaType::Append(trailing_trivia)); - - CompoundAssignment::new(lhs, compound_operator, rhs) -} - // If we have a type like // A | B | { // ... diff --git a/src/formatters/mod.rs b/src/formatters/mod.rs index 4632f77e..57ce20ec 100644 --- a/src/formatters/mod.rs +++ b/src/formatters/mod.rs @@ -6,6 +6,8 @@ pub mod block; pub mod general; #[macro_use] pub mod expression; +#[cfg(any(feature = "luau", feature = "cfxlua"))] +pub mod compound_assignment; pub mod functions; #[cfg(feature = "lua52")] pub mod lua52; diff --git a/src/formatters/stmt.rs b/src/formatters/stmt.rs index e60ff7ec..aad61a14 100644 --- a/src/formatters/stmt.rs +++ b/src/formatters/stmt.rs @@ -1,3 +1,5 @@ +#[cfg(any(feature = "luau", feature = "cfxlua"))] +use crate::formatters::compound_assignment::format_compound_assignment; #[cfg(feature = "lua52")] use crate::formatters::lua52::{format_goto, format_goto_no_trivia, format_label}; #[cfg(feature = "luau")] From f479c22c65c501ca67bdb98a8ea029884cace8c4 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 16:26:30 +0200 Subject: [PATCH 07/16] Handle set constructor field --- src/formatters/functions.rs | 2 ++ src/formatters/stmt.rs | 6 ++++-- src/formatters/table.rs | 10 ++++++++++ src/formatters/trivia_util.rs | 6 ++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/formatters/functions.rs b/src/formatters/functions.rs index 57cb56e9..b6056f5c 100644 --- a/src/formatters/functions.rs +++ b/src/formatters/functions.rs @@ -665,6 +665,8 @@ fn table_constructor_contains_nested_function(table_constructor: &TableConstruct Field::ExpressionKey { key, value, .. } => { contains_nested_function(key) || contains_nested_function(value) } + #[cfg(feature = "cfxlua")] + Field::SetConstructor { .. } => false, Field::NameKey { value, .. } => contains_nested_function(value), other => unreachable!("unknown node {:?}", other), }) diff --git a/src/formatters/stmt.rs b/src/formatters/stmt.rs index aad61a14..06bc9734 100644 --- a/src/formatters/stmt.rs +++ b/src/formatters/stmt.rs @@ -4,8 +4,8 @@ use crate::formatters::compound_assignment::format_compound_assignment; use crate::formatters::lua52::{format_goto, format_goto_no_trivia, format_label}; #[cfg(feature = "luau")] use crate::formatters::luau::{ - format_compound_assignment, format_exported_type_declaration, format_exported_type_function, - format_type_declaration_stmt, format_type_function_stmt, format_type_specifier, + format_exported_type_declaration, format_exported_type_function, format_type_declaration_stmt, + format_type_function_stmt, format_type_specifier, }; use crate::{ context::{create_indent_trivia, create_newline_trivia, Context, FormatNode}, @@ -828,6 +828,8 @@ pub(crate) mod stmt_block { equal, value: format_expression_block(ctx, &value, shape), }, + #[cfg(feature = "cfxlua")] + Field::SetConstructor { dot, name } => Field::SetConstructor { dot, name }, Field::NoKey(expression) => { Field::NoKey(format_expression_block(ctx, &expression, shape)) } diff --git a/src/formatters/table.rs b/src/formatters/table.rs index 831c3954..2da07620 100644 --- a/src/formatters/table.rs +++ b/src/formatters/table.rs @@ -207,6 +207,14 @@ fn format_field( Field::NameKey { key, equal, value } } + #[cfg(feature = "cfxlua")] + Field::SetConstructor { dot, name } => { + trailing_trivia = name.trailing_comments_search(CommentSearch::Single); + let dot = fmt_symbol!(ctx, dot, ".", shape); + let name = format_token_reference(ctx, name, shape); + + Field::SetConstructor { dot, name } + } Field::NoKey(expression) => { trailing_trivia = expression.trailing_comments_search(CommentSearch::Single); @@ -437,6 +445,8 @@ fn should_expand(ctx: &Context, table_constructor: &TableConstructor) -> bool { || expression_is_multiline_function(ctx, value) } Field::NameKey { value, .. } => expression_is_multiline_function(ctx, value), + #[cfg(feature = "cfxlua")] + Field::SetConstructor { .. } => false, Field::NoKey(expression) => expression_is_multiline_function(ctx, expression), other => panic!("unknown node {:?}", other), }; diff --git a/src/formatters/trivia_util.rs b/src/formatters/trivia_util.rs index 341fb6ff..c7139224 100644 --- a/src/formatters/trivia_util.rs +++ b/src/formatters/trivia_util.rs @@ -1064,6 +1064,10 @@ pub fn table_fields_contains_comments(table_constructor: &TableConstructor) -> b Field::NameKey { key, equal, value } => { contains_comments(key) || contains_comments(equal) || contains_comments(value) } + #[cfg(feature = "cfxlua")] + Field::SetConstructor { dot, name } => { + contains_comments(dot) || contains_comments(name) + } Field::NoKey(expression) => contains_comments(expression), other => panic!("unknown node {:?}", other), }; @@ -1077,6 +1081,8 @@ impl GetTrailingTrivia for Field { match self { Field::ExpressionKey { value, .. } => value.trailing_trivia(), Field::NameKey { value, .. } => value.trailing_trivia(), + #[cfg(feature = "cfxlua")] + Field::SetConstructor { name, .. } => GetTrailingTrivia::trailing_trivia(name), Field::NoKey(expression) => expression.trailing_trivia(), other => panic!("unknown node {:?}", other), } From 3f4fec5563b5340c044b468592c7eba65fbff3d7 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 16:33:53 +0200 Subject: [PATCH 08/16] Extract out string literal handling --- src/formatters/general.rs | 204 +++++++++++++++++++------------------- 1 file changed, 102 insertions(+), 102 deletions(-) diff --git a/src/formatters/general.rs b/src/formatters/general.rs index e83ed0b8..8d4a13d1 100644 --- a/src/formatters/general.rs +++ b/src/formatters/general.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "cfxlua")] +use crate::LuaVersion; use crate::{ context::{ create_indent_trivia, create_newline_trivia, line_ending_character, Context, FormatNode, @@ -12,12 +14,15 @@ use crate::{ shape::Shape, QuoteStyle, }; -use full_moon::ast::{ - punctuated::{Pair, Punctuated}, - span::ContainedSpan, -}; use full_moon::node::Node; use full_moon::tokenizer::{StringLiteralQuoteType, Token, TokenKind, TokenReference, TokenType}; +use full_moon::{ + ast::{ + punctuated::{Pair, Punctuated}, + span::ContainedSpan, + }, + ShortString, +}; #[derive(Debug, Clone, Copy)] pub enum FormatTokenType { @@ -91,6 +96,98 @@ pub fn trivia_to_vec( output } +fn format_string_literal( + ctx: &Context, + literal: &ShortString, + multi_line_depth: &usize, + quote_type: &StringLiteralQuoteType, +) -> TokenType { + // CfxLua: if we have a backtick string, don't mess with it + #[cfg(feature = "cfxlua")] + if ctx.config().syntax == LuaVersion::CfxLua + && matches!(quote_type, StringLiteralQuoteType::Backtick) + { + return TokenType::StringLiteral { + literal: literal.clone(), + multi_line_depth: *multi_line_depth, + quote_type: StringLiteralQuoteType::Backtick, + }; + } + + // If we have a brackets string, don't mess with it + if let StringLiteralQuoteType::Brackets = quote_type { + // Convert the string to the correct line endings, by first normalising to LF + // then converting LF to output + let literal = literal + .replace("\r\n", "\n") + .replace('\n', &line_ending_character(ctx.config().line_endings)); + + TokenType::StringLiteral { + literal: literal.into(), + multi_line_depth: *multi_line_depth, + quote_type: StringLiteralQuoteType::Brackets, + } + } else { + // Match all escapes within the string + // Based off https://github.com/prettier/prettier/blob/181a325c1c07f1a4f3738665b7b28288dfb960bc/src/common/util.js#L439 + lazy_static::lazy_static! { + static ref RE: regex::Regex = regex::Regex::new(r#"\\?(["'])|\\([\S\s])"#).unwrap(); + static ref UNNECESSARY_ESCAPES: regex::Regex = regex::Regex::new(r#"^[^\n\r"'0-9\\abfnrtuvxz]$"#).unwrap(); + } + let quote_to_use = get_quote_to_use(ctx, literal); + let literal = RE + .replace_all(literal, |caps: ®ex::Captures| { + let quote = caps.get(1); + let escaped = caps.get(2); + + match quote { + Some(quote) => { + // We have a quote, find what type it is, and see if we need to escape it + // then return the output string + match quote.as_str() { + "'" => { + // Check whether to escape the quote + if let StringLiteralQuoteType::Single = quote_to_use { + String::from("\\'") + } else { + String::from("'") + } + } + "\"" => { + // Check whether to escape the quote + if let StringLiteralQuoteType::Double = quote_to_use { + String::from("\\\"") + } else { + String::from("\"") + } + } + other => unreachable!("unknown quote type {:?}", other), + } + } + None => { + // We have a normal escape + // Test to see if it is necessary, and if not, then unescape it + let text = escaped + .expect("have a match which was neither an escape or a quote") + .as_str(); + if UNNECESSARY_ESCAPES.is_match(text) { + text.to_owned() + } else { + format!("\\{}", text.to_owned()) + } + } + } + }) + .into(); + + TokenType::StringLiteral { + literal, + multi_line_depth: *multi_line_depth, + quote_type: quote_to_use, + } + } +} + /// Formats a Token Node /// Also returns any extra leading or trailing trivia to add for the Token node /// This should only ever be called from format_token_reference @@ -120,104 +217,7 @@ pub fn format_token( literal, multi_line_depth, quote_type, - } => { - // If we have a brackets string, don't mess with it - if let StringLiteralQuoteType::Brackets = quote_type { - // Convert the string to the correct line endings, by first normalising to LF - // then converting LF to output - let literal = literal - .replace("\r\n", "\n") - .replace('\n', &line_ending_character(ctx.config().line_endings)); - - TokenType::StringLiteral { - literal: literal.into(), - multi_line_depth: *multi_line_depth, - quote_type: StringLiteralQuoteType::Brackets, - } - } else { - // Match all escapes within the string - // Based off https://github.com/prettier/prettier/blob/181a325c1c07f1a4f3738665b7b28288dfb960bc/src/common/util.js#L439 - lazy_static::lazy_static! { - static ref RE: regex::Regex = regex::Regex::new(r#"\\?(["'])|\\([\S\s])"#).unwrap(); - static ref UNNECESSARY_ESCAPES: regex::Regex = regex::Regex::new(r#"^[^\n\r"'0-9\\abfnrtuvxz]$"#).unwrap(); - } - let quote_to_use = get_quote_to_use(ctx, literal); - let literal = RE - .replace_all(literal, |caps: ®ex::Captures| { - let quote = caps.get(1); - let escaped = caps.get(2); - - match quote { - Some(quote) => { - // We have a quote, find what type it is, and see if we need to escape it - // then return the output string - match quote.as_str() { - "'" => { - // Check whether to escape the quote - if let StringLiteralQuoteType::Single = quote_to_use { - String::from("\\'") - } else { - String::from("'") - } - } - "\"" => { - // Check whether to escape the quote - if let StringLiteralQuoteType::Double = quote_to_use { - String::from("\\\"") - } else { - String::from("\"") - } - } - other => unreachable!("unknown quote type {:?}", other), - } - } - None => { - // We have a normal escape - // Test to see if it is necessary, and if not, then unescape it - let text = escaped - .expect("have a match which was neither an escape or a quote") - .as_str(); - if UNNECESSARY_ESCAPES.is_match(text) { - text.to_owned() - } else { - format!("\\{}", text.to_owned()) - } - } - } - }) - .into(); - - // If the syntax is CFXLua and the quote type is backtick, retain the use of backticks since those have a runtime functionality in CFXLua - #[cfg(feature = "cfxlua")] - { - use crate::LuaVersion; - if ctx.config().syntax == LuaVersion::CfxLua - && matches!(quote_type, StringLiteralQuoteType::Backtick) - { - TokenType::StringLiteral { - literal, - multi_line_depth: *multi_line_depth, - quote_type: StringLiteralQuoteType::Backtick, - } - } else { - TokenType::StringLiteral { - literal, - multi_line_depth: *multi_line_depth, - quote_type: quote_to_use, - } - } - } - - #[cfg(not(feature = "cfxlua"))] - { - TokenType::StringLiteral { - literal, - multi_line_depth: *multi_line_depth, - quote_type: quote_to_use, - } - } - } - } + } => format_string_literal(ctx, literal, multi_line_depth, quote_type), TokenType::Shebang { line } => { let line = format_single_line_comment_string(line).into(); From 2c474b16b7f46c1df9a070daf3618826cab5cf94 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 16:47:36 +0200 Subject: [PATCH 09/16] Ensure cfxlua is enabled in "All" mode, and fix symbol parsing --- src/formatters/general.rs | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/formatters/general.rs b/src/formatters/general.rs index 8d4a13d1..824f32e5 100644 --- a/src/formatters/general.rs +++ b/src/formatters/general.rs @@ -47,7 +47,7 @@ macro_rules! fmt_symbol { $crate::formatters::general::format_symbol( $ctx, $token, - &TokenReference::symbol($x).unwrap(), + &TokenReference::symbol_specific_lua_version($x, $ctx.config().syntax.into()).unwrap(), $shape, ) }; diff --git a/src/lib.rs b/src/lib.rs index a6736a86..cba573a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,7 +50,7 @@ pub enum LuaVersion { impl From for full_moon::LuaVersion { fn from(val: LuaVersion) -> Self { match val { - LuaVersion::All => full_moon::LuaVersion::new(), + LuaVersion::All => full_moon::LuaVersion::new().with_cfxlua(), LuaVersion::Lua51 => full_moon::LuaVersion::lua51(), #[cfg(feature = "lua52")] LuaVersion::Lua52 => full_moon::LuaVersion::lua52(), From 9976a3f2925f938c417f2472bd6efaf18d025c99 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 16:48:41 +0200 Subject: [PATCH 10/16] Update snapshots --- ..._cfxlua@addition_assignment_operator.lua.snap | 9 +++++++++ .../tests__cfxlua@ampersand_equal.lua.snap | 8 ++++++++ ...xlua@bitwise_xor_assignment_operator.lua.snap | 8 ++++++++ ...__cfxlua@compile_time_jenkins_hashes.lua.snap | 7 +++++++ ..._cfxlua@division_assignment_operator.lua.snap | 8 ++++++++ ...ts__cfxlua@double_greater_than_equal.lua.snap | 8 ++++++++ ...tests__cfxlua@double_less_than_equal.lua.snap | 8 ++++++++ .../tests__cfxlua@each_iteration.lua.snap | 10 ++++++++++ ...a@multiplication_assignment_operator.lua.snap | 8 ++++++++ .../snapshots/tests__cfxlua@pipe_equal.lua.snap | 8 ++++++++ .../tests__cfxlua@question_mark_dot.lua.snap | 16 ++++++++++++++++ .../tests__cfxlua@set_constructor.lua.snap | 7 +++++++ ...xlua@subtraction_assignment_operator.lua.snap | 8 ++++++++ 13 files changed, 113 insertions(+) create mode 100644 tests/snapshots/tests__cfxlua@addition_assignment_operator.lua.snap create mode 100644 tests/snapshots/tests__cfxlua@ampersand_equal.lua.snap create mode 100644 tests/snapshots/tests__cfxlua@bitwise_xor_assignment_operator.lua.snap create mode 100644 tests/snapshots/tests__cfxlua@compile_time_jenkins_hashes.lua.snap create mode 100644 tests/snapshots/tests__cfxlua@division_assignment_operator.lua.snap create mode 100644 tests/snapshots/tests__cfxlua@double_greater_than_equal.lua.snap create mode 100644 tests/snapshots/tests__cfxlua@double_less_than_equal.lua.snap create mode 100644 tests/snapshots/tests__cfxlua@each_iteration.lua.snap create mode 100644 tests/snapshots/tests__cfxlua@multiplication_assignment_operator.lua.snap create mode 100644 tests/snapshots/tests__cfxlua@pipe_equal.lua.snap create mode 100644 tests/snapshots/tests__cfxlua@question_mark_dot.lua.snap create mode 100644 tests/snapshots/tests__cfxlua@set_constructor.lua.snap create mode 100644 tests/snapshots/tests__cfxlua@subtraction_assignment_operator.lua.snap diff --git a/tests/snapshots/tests__cfxlua@addition_assignment_operator.lua.snap b/tests/snapshots/tests__cfxlua@addition_assignment_operator.lua.snap new file mode 100644 index 00000000..9b7a3994 --- /dev/null +++ b/tests/snapshots/tests__cfxlua@addition_assignment_operator.lua.snap @@ -0,0 +1,9 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::CfxLua)" +input_file: tests/inputs-cfxlua/addition_assignment_operator.lua +snapshot_kind: text +--- +local index = 1 + +index += 1 diff --git a/tests/snapshots/tests__cfxlua@ampersand_equal.lua.snap b/tests/snapshots/tests__cfxlua@ampersand_equal.lua.snap new file mode 100644 index 00000000..3578d957 --- /dev/null +++ b/tests/snapshots/tests__cfxlua@ampersand_equal.lua.snap @@ -0,0 +1,8 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::CfxLua)" +input_file: tests/inputs-cfxlua/ampersand_equal.lua +snapshot_kind: text +--- +local num = 6 +num &= 3 diff --git a/tests/snapshots/tests__cfxlua@bitwise_xor_assignment_operator.lua.snap b/tests/snapshots/tests__cfxlua@bitwise_xor_assignment_operator.lua.snap new file mode 100644 index 00000000..98645335 --- /dev/null +++ b/tests/snapshots/tests__cfxlua@bitwise_xor_assignment_operator.lua.snap @@ -0,0 +1,8 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::CfxLua)" +input_file: tests/inputs-cfxlua/bitwise_xor_assignment_operator.lua +snapshot_kind: text +--- +local num = 5 +num ^= 3 diff --git a/tests/snapshots/tests__cfxlua@compile_time_jenkins_hashes.lua.snap b/tests/snapshots/tests__cfxlua@compile_time_jenkins_hashes.lua.snap new file mode 100644 index 00000000..249e71a7 --- /dev/null +++ b/tests/snapshots/tests__cfxlua@compile_time_jenkins_hashes.lua.snap @@ -0,0 +1,7 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::CfxLua)" +input_file: tests/inputs-cfxlua/compile_time_jenkins_hashes.lua +snapshot_kind: text +--- +print(`Hello, World!`) diff --git a/tests/snapshots/tests__cfxlua@division_assignment_operator.lua.snap b/tests/snapshots/tests__cfxlua@division_assignment_operator.lua.snap new file mode 100644 index 00000000..224430e5 --- /dev/null +++ b/tests/snapshots/tests__cfxlua@division_assignment_operator.lua.snap @@ -0,0 +1,8 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::CfxLua)" +input_file: tests/inputs-cfxlua/division_assignment_operator.lua +snapshot_kind: text +--- +local num = 10 +num /= 2 diff --git a/tests/snapshots/tests__cfxlua@double_greater_than_equal.lua.snap b/tests/snapshots/tests__cfxlua@double_greater_than_equal.lua.snap new file mode 100644 index 00000000..8cc666d6 --- /dev/null +++ b/tests/snapshots/tests__cfxlua@double_greater_than_equal.lua.snap @@ -0,0 +1,8 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::CfxLua)" +input_file: tests/inputs-cfxlua/double_greater_than_equal.lua +snapshot_kind: text +--- +local num = 4 +num >>= 2 diff --git a/tests/snapshots/tests__cfxlua@double_less_than_equal.lua.snap b/tests/snapshots/tests__cfxlua@double_less_than_equal.lua.snap new file mode 100644 index 00000000..e1c34bca --- /dev/null +++ b/tests/snapshots/tests__cfxlua@double_less_than_equal.lua.snap @@ -0,0 +1,8 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::CfxLua)" +input_file: tests/inputs-cfxlua/double_less_than_equal.lua +snapshot_kind: text +--- +local num = 1 +num <<= 2 diff --git a/tests/snapshots/tests__cfxlua@each_iteration.lua.snap b/tests/snapshots/tests__cfxlua@each_iteration.lua.snap new file mode 100644 index 00000000..2dfac16e --- /dev/null +++ b/tests/snapshots/tests__cfxlua@each_iteration.lua.snap @@ -0,0 +1,10 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::CfxLua)" +input_file: tests/inputs-cfxlua/each_iteration.lua +snapshot_kind: text +--- +local t = { 1, 2, 3 } +for k, v in each(t) do + print(k, v) +end diff --git a/tests/snapshots/tests__cfxlua@multiplication_assignment_operator.lua.snap b/tests/snapshots/tests__cfxlua@multiplication_assignment_operator.lua.snap new file mode 100644 index 00000000..bdd7aeef --- /dev/null +++ b/tests/snapshots/tests__cfxlua@multiplication_assignment_operator.lua.snap @@ -0,0 +1,8 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::CfxLua)" +input_file: tests/inputs-cfxlua/multiplication_assignment_operator.lua +snapshot_kind: text +--- +local base = 10 +base *= 2 diff --git a/tests/snapshots/tests__cfxlua@pipe_equal.lua.snap b/tests/snapshots/tests__cfxlua@pipe_equal.lua.snap new file mode 100644 index 00000000..802a2d1c --- /dev/null +++ b/tests/snapshots/tests__cfxlua@pipe_equal.lua.snap @@ -0,0 +1,8 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::CfxLua)" +input_file: tests/inputs-cfxlua/pipe_equal.lua +snapshot_kind: text +--- +local num = 4 +num |= 1 diff --git a/tests/snapshots/tests__cfxlua@question_mark_dot.lua.snap b/tests/snapshots/tests__cfxlua@question_mark_dot.lua.snap new file mode 100644 index 00000000..ed4ad547 --- /dev/null +++ b/tests/snapshots/tests__cfxlua@question_mark_dot.lua.snap @@ -0,0 +1,16 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::CfxLua)" +input_file: tests/inputs-cfxlua/question_mark_dot.lua +snapshot_kind: text +--- +local a = { + b = 1, + c = 2, + d = { + e = 3, + }, +} + +local x = a?.b +local y = a?.d?.e diff --git a/tests/snapshots/tests__cfxlua@set_constructor.lua.snap b/tests/snapshots/tests__cfxlua@set_constructor.lua.snap new file mode 100644 index 00000000..71ea3549 --- /dev/null +++ b/tests/snapshots/tests__cfxlua@set_constructor.lua.snap @@ -0,0 +1,7 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::CfxLua)" +input_file: tests/inputs-cfxlua/set_constructor.lua +snapshot_kind: text +--- +t = { .a, b = false } diff --git a/tests/snapshots/tests__cfxlua@subtraction_assignment_operator.lua.snap b/tests/snapshots/tests__cfxlua@subtraction_assignment_operator.lua.snap new file mode 100644 index 00000000..221a8776 --- /dev/null +++ b/tests/snapshots/tests__cfxlua@subtraction_assignment_operator.lua.snap @@ -0,0 +1,8 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::CfxLua)" +input_file: tests/inputs-cfxlua/subtraction_assignment_operator.lua +snapshot_kind: text +--- +local ten = 10 +ten -= 5 From c412437553b9f2a875dd4f2b0be2734a9e2db851 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 16:53:36 +0200 Subject: [PATCH 11/16] Handle in unpacking --- src/formatters/assignment.rs | 11 ++++++++++- tests/snapshots/tests__cfxlua@in_unpacking.lua.snap | 13 +++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/snapshots/tests__cfxlua@in_unpacking.lua.snap diff --git a/src/formatters/assignment.rs b/src/formatters/assignment.rs index 56bf9340..ddade8c8 100644 --- a/src/formatters/assignment.rs +++ b/src/formatters/assignment.rs @@ -1,6 +1,6 @@ #[cfg(feature = "luau")] use full_moon::ast::luau::TypeSpecifier; -use full_moon::tokenizer::{Token, TokenReference}; +use full_moon::tokenizer::{Symbol, Token, TokenReference}; use full_moon::{ ast::{ punctuated::{Pair, Punctuated}, @@ -451,6 +451,15 @@ pub fn format_local_assignment_no_trivia( Some(1), ); let mut equal_token = fmt_symbol!(ctx, assignment.equal_token().unwrap(), " = ", shape); + + // In CfxLua, the equal token could actually be an "in" token for table unpacking + #[cfg(feature = "cfxlua")] + if let TokenType::Symbol { symbol: Symbol::In } = + assignment.equal_token().unwrap().token_type() + { + equal_token = fmt_symbol!(ctx, assignment.equal_token().unwrap(), " in ", shape); + } + let mut expr_list = format_punctuated( ctx, assignment.expressions(), diff --git a/tests/snapshots/tests__cfxlua@in_unpacking.lua.snap b/tests/snapshots/tests__cfxlua@in_unpacking.lua.snap new file mode 100644 index 00000000..a11fba18 --- /dev/null +++ b/tests/snapshots/tests__cfxlua@in_unpacking.lua.snap @@ -0,0 +1,13 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::CfxLua)" +input_file: tests/inputs-cfxlua/in_unpacking.lua +snapshot_kind: text +--- +local t = { + a = 1, + b = 2, + c = 3, +} + +local a, b, c in t From 29d4dd91f50fdbb6534ddf447e1fe9c4fda51233 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 16:53:43 +0200 Subject: [PATCH 12/16] Update snapshot --- .../tests__cfxlua@c_style_comments.lua.snap | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/snapshots/tests__cfxlua@c_style_comments.lua.snap diff --git a/tests/snapshots/tests__cfxlua@c_style_comments.lua.snap b/tests/snapshots/tests__cfxlua@c_style_comments.lua.snap new file mode 100644 index 00000000..4f89f589 --- /dev/null +++ b/tests/snapshots/tests__cfxlua@c_style_comments.lua.snap @@ -0,0 +1,13 @@ +--- +source: tests/tests.rs +expression: "format(&contents, LuaVersion::CfxLua)" +input_file: tests/inputs-cfxlua/c_style_comments.lua +snapshot_kind: text +--- +print("Hello, World!") +/* Comment */ +/* +Multi +Line +Comment +*/ From dc0e554969dc88d7c8c51805fc7ce310f3cf6057 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 16:54:02 +0200 Subject: [PATCH 13/16] Formatting --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 24c0ec9a..7e76d868 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ lua52 = ["full_moon/lua52"] lua53 = ["lua52", "full_moon/lua53"] lua54 = ["lua53", "full_moon/lua54"] luajit = ["full_moon/luajit"] -cfxlua = ["lua54", "full_moon/cfxlua"] +cfxlua = ["lua54", "full_moon/cfxlua"] editorconfig = ["ec4rs"] [dependencies] From 30d441ec1a73cbba05ea3c72094da97cd8e21c56 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 16:56:07 +0200 Subject: [PATCH 14/16] Update changelog and readme --- CHANGELOG.md | 1 + README.md | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc6a11d4..411001cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Luau: Added support for parsing user-defined type functions ([#938](https://github.com/JohnnyMorganz/StyLua/issues/938)) - Luau: Added support for parsing attributes (`@native` / `@deprecated`) on functions +- Added support for CfxLua (FiveM) syntax formatting. This is available with `syntax = "cfxlua"` ([#855](https://github.com/JohnnyMorganz/StyLua/issues/855)) ### Fixed diff --git a/README.md b/README.md index 17ac8118..e11867f9 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ -A deterministic code formatter for Lua 5.1, 5.2, 5.3, 5.4, LuaJIT and [Luau](https://luau.org/), built using [full-moon](https://github.com/Kampfkarren/full-moon). +A deterministic code formatter for Lua 5.1, 5.2, 5.3, 5.4, LuaJIT, [Luau](https://luau.org/) and [CfxLua/FiveM Lua](https://docs.fivem.net/docs/scripting-manual/runtimes/lua/), built using [full-moon](https://github.com/Kampfkarren/full-moon). StyLua is inspired by the likes of [prettier](https://github.com/prettier/prettier), it parses your Lua codebase, and prints it back out from scratch, enforcing a consistent code style. @@ -285,7 +285,7 @@ StyLua only offers the following options: | Option | Default | Description | | ---------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `syntax` | `All` | Specify a disambiguation for the style of Lua syntax being formatted. Possible options: `All` (default), `Lua51`, `Lua52`, `Lua53`, `Lua54`, `LuaJIT`, `Luau` | +| `syntax` | `All` | Specify a disambiguation for the style of Lua syntax being formatted. Possible options: `All` (default), `Lua51`, `Lua52`, `Lua53`, `Lua54`, `LuaJIT`, `Luau`, `CfxLua` | | `column_width` | `120` | Approximate line length for printing. Used as a guide for line wrapping - this is not a hard requirement: lines may fall under or over the limit. | | `line_endings` | `Unix` | Line endings type. Possible options: `Unix` (LF) or `Windows` (CRLF) | | `indent_type` | `Tabs` | Indent type. Possible options: `Tabs` or `Spaces` | From 1e68ee90303273b6711335477450d28a30bec915 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 16:59:41 +0200 Subject: [PATCH 15/16] Fix compilation --- src/formatters/assignment.rs | 4 +++- src/lib.rs | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/formatters/assignment.rs b/src/formatters/assignment.rs index ddade8c8..d1bb8d89 100644 --- a/src/formatters/assignment.rs +++ b/src/formatters/assignment.rs @@ -1,6 +1,8 @@ #[cfg(feature = "luau")] use full_moon::ast::luau::TypeSpecifier; -use full_moon::tokenizer::{Symbol, Token, TokenReference}; +#[cfg(feature = "cfxlua")] +use full_moon::tokenizer::Symbol; +use full_moon::tokenizer::{Token, TokenReference}; use full_moon::{ ast::{ punctuated::{Pair, Punctuated}, diff --git a/src/lib.rs b/src/lib.rs index cba573a7..b637757d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,7 +50,10 @@ pub enum LuaVersion { impl From for full_moon::LuaVersion { fn from(val: LuaVersion) -> Self { match val { + #[cfg(feature = "cfxlua")] LuaVersion::All => full_moon::LuaVersion::new().with_cfxlua(), + #[cfg(not(feature = "cfxlua"))] + LuaVersion::All => full_moon::LuaVersion::new(), LuaVersion::Lua51 => full_moon::LuaVersion::lua51(), #[cfg(feature = "lua52")] LuaVersion::Lua52 => full_moon::LuaVersion::lua52(), From de5db5595cf0c5059363a7f48e89069eecee56b2 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Mon, 21 Apr 2025 17:00:45 +0200 Subject: [PATCH 16/16] Enable compound op for cfxlua --- src/formatters/stmt.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/formatters/stmt.rs b/src/formatters/stmt.rs index 06bc9734..7cbe2171 100644 --- a/src/formatters/stmt.rs +++ b/src/formatters/stmt.rs @@ -1063,7 +1063,7 @@ pub(crate) mod stmt_block { let block = format_block(ctx, while_block.block(), block_shape); Stmt::While(while_block.to_owned().with_block(block)) } - #[cfg(feature = "luau")] + #[cfg(any(feature = "luau", feature = "cfxlua"))] Stmt::CompoundAssignment(compound_assignment) => { let rhs = format_expression_block(ctx, compound_assignment.rhs(), block_shape); Stmt::CompoundAssignment(compound_assignment.to_owned().with_rhs(rhs)) @@ -1119,7 +1119,7 @@ pub fn format_stmt(ctx: &Context, stmt: &Stmt, shape: Shape) -> Stmt { NumericFor = format_numeric_for, Repeat = format_repeat_block, While = format_while_block, - #[cfg(feature = "luau")] CompoundAssignment = format_compound_assignment, + #[cfg(any(feature = "luau", feature = "cfxlua"))] CompoundAssignment = format_compound_assignment, #[cfg(feature = "luau")] ExportedTypeDeclaration = format_exported_type_declaration, #[cfg(feature = "luau")] TypeDeclaration = format_type_declaration_stmt, #[cfg(feature = "luau")] ExportedTypeFunction = format_exported_type_function,