diff --git a/.github/workflows/spec.yml b/.github/workflows/spec.yml index 80d5e92..6549862 100644 --- a/.github/workflows/spec.yml +++ b/.github/workflows/spec.yml @@ -48,7 +48,7 @@ jobs: - name: Compile parsers run: | - nvim --headless -c "TSInstallSync ruby python lua javascript julia yaml sql r" -c "q" + nvim --headless -c "TSInstallSync ruby python lua javascript julia yaml sql r rust" -c "q" - name: Tests env: ci: "1" diff --git a/README.md b/README.md index cf81b2d..917baf2 100644 --- a/README.md +++ b/README.md @@ -262,7 +262,7 @@ Builtin actions are all higher-order functions so they can easily have options o | `cycle_case()` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | | | ✅ | ✅ | | `cycle_quotes()` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | | | | ✅ | | `toggle_multiline()` | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | | | -| `toggle_operator()` | | ✅ | ✅ | ✅ | ✅ | ✅ | | | | | ✅ | +| `toggle_operator()` | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | | | ✅ | | `toggle_int_readability()` | | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ | | | | | `toggle_block()` | | ✅ | | | | | | | | | | | if/else <-> ternery | | ✅ | | | ✅ | | | | | | | diff --git a/lua/ts-node-action/filetypes/rust.lua b/lua/ts-node-action/filetypes/rust.lua index 2c87bf3..d503a53 100644 --- a/lua/ts-node-action/filetypes/rust.lua +++ b/lua/ts-node-action/filetypes/rust.lua @@ -1,26 +1,90 @@ local actions = require("ts-node-action.actions") +local operators = { + -- assignment + ["+="] = "-=", + ["-="] = "+=", + ["%="] = "/=", + ["/="] = "%=", + -- bitwise assignment + ["&="] = "|=", + ["|="] = "^=", + ["^="] = "&=", + -- comparison + ["!="] = "==", + ["=="] = "!=", + [">"] = "<", + ["<"] = ">", + [">="] = "<=", + ["<="] = ">=", + -- shift + ["<<"] = ">>", + [">>"] = "<<", + -- shift assignment + [">>="] = "<<=", + ["<<="] = ">>=", + -- arithmetic + ["+"] = "-", + ["-"] = "+", + ["*"] = "/", + ["/"] = "*", + -- bitwise + ["|"] = "&", + ["&"] = "|", + -- logical + ["||"] = "&&", + ["&&"] = "||", +} + local padding = { + ["{"] = "%s ", + ["}"] = { + " %s", + ["{"] = "%s", + [","] = "%s", + [";"] = "%s", + ["prev_nil"] = "%s", + }, + ["let"] = "%s ", + ["as"] = " %s ", + ["in"] = { " %s ", ["prev_nil"] = "%s ", }, + ["="] = " %s ", + [":"] = "%s ", + [";"] = "%s ", + [","] = "%s ", +} +local padding_use_list = { + ["{"] = "%s", + ["}"] = "%s", [","] = "%s ", - [":"] = "%s ", - ["{"] = "%s ", - ["=>"] = " %s ", - ["="] = " %s ", - ["}"] = " %s", - ["+"] = " %s ", - ["-"] = " %s ", - ["*"] = " %s ", - ["/"] = " %s ", +} + +local uncollapsible = { + ["string_literal"] = true, + ["let_declaration"] = true, + ["reference_type"] = true, + ["reference_expression"] = true, + ["type_case_expression"] = true, + ["macro_invocation"] = true, + ["macro"] = true, + ["for_expression"] = true, + ["range_expression"] = true, + ["line_comment"] = true, } return { - ["field_declaration_list"] = actions.toggle_multiline(padding), - ["parameters"] = actions.toggle_multiline(padding), - ["enum_variant_list"] = actions.toggle_multiline(padding), - ["block"] = actions.toggle_multiline(padding), - ["array_expression"] = actions.toggle_multiline(padding), - ["tuple_expression"] = actions.toggle_multiline(padding), - ["tuple_pattern"] = actions.toggle_multiline(padding), - ["boolean_literal"] = actions.toggle_boolean(), - ["integer_literal"] = actions.toggle_int_readability(), + ["boolean_literal"] = actions.toggle_boolean(), + ["integer_literal"] = actions.toggle_int_readability(), + ["binary_expression"] = actions.toggle_operator(operators), + ["compound_assignment_expr"] = actions.toggle_operator(operators), + ["use_list"] = actions.toggle_multiline(padding_use_list, uncollapsible), + ["block"] = actions.toggle_multiline(padding, uncollapsible), + ["parameters"] = actions.toggle_multiline(padding, uncollapsible), + ["arguments"] = actions.toggle_multiline(padding, uncollapsible), + ["array_expression"] = actions.toggle_multiline(padding, uncollapsible), + ["tuple_expression"] = actions.toggle_multiline(padding, uncollapsible), + ["tuple_pattern"] = actions.toggle_multiline(padding, uncollapsible), + ["enum_variant_list"] = actions.toggle_multiline(padding, uncollapsible), + ["field_initializer_list"] = actions.toggle_multiline(padding, uncollapsible), + ["field_declaration_list"] = actions.toggle_multiline(padding, uncollapsible), } diff --git a/spec/filetypes/rust_spec.lua b/spec/filetypes/rust_spec.lua new file mode 100644 index 0000000..70ae282 --- /dev/null +++ b/spec/filetypes/rust_spec.lua @@ -0,0 +1,510 @@ +dofile("spec/spec_helper.lua") + +local Helper = SpecHelper.new("rust", { shiftwidth = 4 }) + +describe("toggle_boolean", function() + it("toggles 'true' and 'false'", function() + assert.are.same({ "let i = true;" }, Helper:call({ "let i = false;" }, { 1, 9 })) + assert.are.same({ "let i = false;" }, Helper:call({ "let i = true;" }, { 1, 9 })) + end) +end) + +describe("toggle_int_readability", function() + it("1 million to friendly", function() + assert.are.same({ "x = 1000000" }, Helper:call({ + "x = 1_000_000",}, { 1, 5 })) + end) + + it("1 million to unfriendly", function() + assert.are.same({ "x = 1_000_000" }, Helper:call({ + "x = 1000000",}, { 1, 5 })) + end) +end) + +describe("operator", function() + -- assignment + it("toggles '-=' and '+='", function() + assert.are.same({ "i += 8" }, Helper:call({ "i -= 8" }, { 1, 3 })) + assert.are.same({ "i -= 8" }, Helper:call({ "i += 8" }, { 1, 3 })) + end) + + it("toggles '/=' and '%='", function() + assert.are.same({ "i %= 8" }, Helper:call({ "i /= 8" }, { 1, 3 })) + assert.are.same({ "i /= 8" }, Helper:call({ "i %= 8" }, { 1, 3 })) + end) + + -- bitwise assignment + it("toggles '&=' and '|=' and '^='", function() + assert.are.same({ "i |= 8" }, Helper:call({ "i &= 8" }, { 1, 3 })) + assert.are.same({ "i ^= 8" }, Helper:call({ "i |= 8" }, { 1, 3 })) + assert.are.same({ "i &= 8" }, Helper:call({ "i ^= 8" }, { 1, 3 })) + end) + + -- comparison + it("toggles '==' and '!='", function() + assert.are.same({ "i == 8" }, Helper:call({ "i != 8" }, { 1, 3 })) + assert.are.same({ "i != 8" }, Helper:call({ "i == 8" }, { 1, 3 })) + end) + + it("toggles '>' and '<'", function() + assert.are.same({ "i > 8" }, Helper:call({ "i < 8" }, { 1, 3 })) + assert.are.same({ "i < 8" }, Helper:call({ "i > 8" }, { 1, 3 })) + end) + + it("toggles '<= and '>='", function() + assert.are.same({ "i <= 8" }, Helper:call({ "i >= 8" }, { 1, 3 })) + assert.are.same({ "i >= 8" }, Helper:call({ "i <= 8" }, { 1, 3 })) + end) + + -- shift + it("toggles '<<' and '>>'", function() + assert.are.same({ "i << 8" }, Helper:call({ "i >> 8" }, { 1, 3 })) + assert.are.same({ "i >> 8" }, Helper:call({ "i << 8" }, { 1, 3 })) + end) + + -- shift assignment + it("toggles '<<=' and '>>='", function() + assert.are.same({ "i >>= 8" }, Helper:call({ "i <<= 8" }, { 1, 3 })) + assert.are.same({ "i <<= 8" }, Helper:call({ "i >>= 8" }, { 1, 3 })) + end) + + -- arithmetic + it("toggles '+' and '-'", function() + assert.are.same({ "i + 8" }, Helper:call({ "i - 8" }, { 1, 3 })) + assert.are.same({ "i - 8" }, Helper:call({ "i + 8" }, { 1, 3 })) + end) + + it("toggles '*' and '/'", function() + assert.are.same({ "i * 8" }, Helper:call({ "i / 8" }, { 1, 3 })) + assert.are.same({ "i / 8" }, Helper:call({ "i * 8" }, { 1, 3 })) + end) + + -- bitwise + it("toggles '|' and '&'", function() + assert.are.same({ "i | 8" }, Helper:call({ "i & 8" }, { 1, 3 })) + assert.are.same({ "i & 8" }, Helper:call({ "i | 8" }, { 1, 3 })) + end) + + -- logical + it("toggles '||' and '&&'", function() + assert.are.same({ "i || 8" }, Helper:call({ "i && 8" }, { 1, 3 })) + assert.are.same({ "i && 8" }, Helper:call({ "i || 8" }, { 1, 3 })) + end) +end) + +describe("toggle_multiline", function() + it("use_list", function() + assert.are.same( + { + "use std::collections::{", + " HashMap,", + " HashSet", + "};", + }, + Helper:call( + { + "use std::collections::{HashMap, HashSet};", + }, + { 1, 23 } + ) + ) + + assert.are.same( + { + "use std::collections::{HashMap, HashSet};", + }, + Helper:call( + { + "use std::collections::{", + " HashMap,", + " HashSet", + "};", + }, + { 1, 23 } + ) + ) + end) + + it("block", function() + assert.are.same( + { + "fn main() {", + " println!(\"main:\\tmodule '{}', file '{}'\", module_path!(), file!());", + " submod::hi();", + "}" + }, + Helper:call( + { + "fn main() { println!(\"main:\\tmodule '{}', file '{}'\", module_path!(), file!()); submod::hi(); }" + }, + { 1, 11 } + ) + ) + + assert.are.same( + { + "fn main() { println!(\"main:\\tmodule '{}', file '{}'\", module_path!(), file!()); submod::hi(); }" + }, + Helper:call( + { + "fn main() {", + " println!(\"main:\\tmodule '{}', file '{}'\", module_path!(), file!());", + " submod::hi();", + "}" + }, + { 1, 11 } + ) + ) + end) + + it("block fix #41 - as", function() + assert.are.same( + { + "{", + " visitor.visit_char(self.parse_u8()? as char)", + "}", + }, + Helper:call( + { + "{ visitor.visit_char(self.parse_u8()? as char) }", + }, + { 1, 1 } + ) + ) + assert.are.same( + { + "{ visitor.visit_char(self.parse_u8()? as char) }", + }, + Helper:call( + { + "{", + " visitor.visit_char(self.parse_u8()? as char)", + "}", + }, + { 1, 1 } + ) + ) + end) + + it("block fix #41 - let", function() + assert.are.same( + { + "fn test() { let foo = stuff(); }" + }, + Helper:call( + { + "fn test() {", + " let foo = stuff();", + "}", + }, + { 1, 11 } + ) + ) + end) + + it("block fix #41 - &mut (reference_type)", function() + assert.are.same( + { + "fn test(", + " foo: &mut Foo", + ") {", + "}" + }, + Helper:call( + { + "fn test(foo: &mut Foo) {", + "}", + }, + { 1, 8 } + ) + ) + end) + + it("block fix #41 - &mut (reference_expression)", function() + assert.are.same( + { + "value.serialize(&mut serializer)" + }, + Helper:call( + { + "value.serialize(", + " &mut serializer", + ")", + }, + { 1, 16 } + ) + ) + end) + + it("parameters", function() + assert.are.same( + { + "fn main(", + " first: u8,", + " second: u8", + ") {", + " submod::hi();", + "}" + }, + Helper:call( + { + "fn main(first: u8, second: u8) {", + " submod::hi();", + "}" + }, + { 1, 8 } + ) + ) + + assert.are.same( + { + "fn main(first: u8, second: u8) {", + " submod::hi();", + "}" + }, + Helper:call( + { + "fn main(", + " first: u8,", + " second: u8", + ") {", + " submod::hi();", + "}" + }, + { 1, 8 } + ) + ) + end) + + it("arguments", function() + assert.are.same( + { + "list.insert_at_ith(", + " 0,", + " first_value", + ");" + }, + Helper:call( + { + "list.insert_at_ith(0, first_value);", + }, + { 1, 19 } + ) + ) + + assert.are.same( + { + "list.insert_at_ith(0, first_value);", + }, + Helper:call( + { + "list.insert_at_ith(", + " 0,", + " first_value", + ");", + }, + { 1, 19 } + ) + ) + end) + + it("array_expression", function() + assert.are.same( + { + "let bytes: [u8; 3] = [", + " 1,", + " 2,", + " 3", + "];", + }, + Helper:call( + { + "let bytes: [u8; 3] = [1, 2, 3];" + }, + { 1, 22 } + ) + ) + + assert.are.same( + { + "let bytes: [u8; 3] = [1, 2, 3];", + }, + Helper:call( + { + "let bytes: [u8; 3] = [", + " 1,", + " 2,", + " 3", + "];", + }, + { 1, 22 } + ) + ) + end) + + it("tuple_expression", function() + assert.are.same( + { + "let foo = (", + " 0.0,", + " 4.5", + ")" + }, + Helper:call( + { + "let foo = (0.0, 4.5)" + }, + { 1, 11 } + ) + ) + + assert.are.same( + { + "let foo = (0.0, 4.5)", + }, + Helper:call( + { + "let foo = (", + " 0.0,", + " 4.5", + ")", + }, + { 1, 11 } + ) + ) + end) + + it("tuple_pattern", function() + assert.are.same( + { + "let (", + "a,", + "b", + ") = foo;" + }, + Helper:call( + { + "let (a, b) = foo;" + }, + { 1, 5 } + ) + ) + + assert.are.same( + { + "let (a, b) = foo;", + }, + Helper:call( + { + "let (", + " a,", + " b", + ") = foo;", + }, + { 1, 5 } + ) + ) + end) + + it("enum_variant_list", function() + assert.are.same( + { + "enum Foo {", + " A,", + " B,", + " C", + "}", + }, + Helper:call( + { + "enum Foo { A, B, C }", + }, + { 1, 10 } + ) + ) + + assert.are.same( + { + "enum Foo { A, B, C }", + }, + Helper:call( + { + "enum Foo {", + " A,", + " B,", + " C", + "}", + }, + { 1, 10 } + ) + ) + end) + + it("field_initializer_list", function() + assert.are.same( + { + "Foo {", + " a: 1,", + " b: 2,", + " c: 3", + "}", + }, + Helper:call( + { + "Foo { a: 1, b: 2, c: 3 }", + }, + { 1, 5 } + ) + ) + + assert.are.same( + { + "Foo { a: 1, b: 2, c: 3 }", + }, + Helper:call( + { + "Foo {", + " a: 1,", + " b: 2,", + " c: 3", + "}", + }, + { 1, 5 } + ) + ) + end) + + it("field_declaration_list", function() + assert.are.same( + { + "struct Foo {", + " a: i32,", + " b: i32,", + " c: i32", + "}", + }, + Helper:call( + { + "struct Foo { a: i32, b: i32, c: i32 }", + }, + { 1, 12 } + ) + ) + + assert.are.same( + { + "struct Foo { a: i32, b: i32, c: i32 }", + }, + Helper:call( + { + "struct Foo {", + " a: i32,", + " b: i32,", + " c: i32", + "}", + }, + { 1, 12 } + ) + ) + end) +end)