diff --git a/lua/ts-node-action/actions/toggle_multiline.lua b/lua/ts-node-action/actions/toggle_multiline.lua index eb677e7..47f45c0 100644 --- a/lua/ts-node-action/actions/toggle_multiline.lua +++ b/lua/ts-node-action/actions/toggle_multiline.lua @@ -1,35 +1,35 @@ local helpers = require("ts-node-action.helpers") -local function collapse_child_nodes(padding) - padding = padding or {} +---@param padding table Used to specify string formatting for unnamed nodes +---@param uncollapsible table Used to specify "base" types that shouldn't be collapsed further. +---@return function +local function collapse_child_nodes(padding, uncollapsible) + local function can_be_collapsed(child) + return child:named_child_count() > 0 and not uncollapsible[child:type()] + end - local function collapse(node) + return function(node) local replacement = {} - local child_text - local context for child, _ in node:iter_children() do - if child:named_child_count() > 0 then - child_text = collapse(child) - if child_text == nil then - return - end + if can_be_collapsed(child) then + local child_text = collapse_child_nodes(padding, uncollapsible)(child) + if not child_text then return end -- We found a comment, abort + table.insert(replacement, child_text) - context = nil - elseif child:type() == "comment" then + elseif child:type() == "comment" then -- TODO: use child:extra() when that API gets merged into stable return else - context = helpers.padded_node_text(child, padding, context) - table.insert(replacement, context) + table.insert(replacement, helpers.padded_node_text(child, padding)) end end return table.concat(vim.tbl_flatten(replacement)) end - - return collapse end +---@param node TSNode +---@return table local function expand_child_nodes(node) local replacement = {} @@ -50,13 +50,17 @@ local function expand_child_nodes(node) return replacement end -return function(padding) - padding = padding or {} +---@param padding table +---@param uncollapsible table +---@return table +return function(padding, uncollapsible) + padding = padding or {} + uncollapsible = uncollapsible or {} local function action(node) local fn if helpers.node_is_multiline(node) then - fn = collapse_child_nodes(padding) + fn = collapse_child_nodes(padding, uncollapsible) else fn = expand_child_nodes end diff --git a/lua/ts-node-action/filetypes/lua.lua b/lua/ts-node-action/filetypes/lua.lua index 1062ca3..984e63b 100644 --- a/lua/ts-node-action/filetypes/lua.lua +++ b/lua/ts-node-action/filetypes/lua.lua @@ -28,20 +28,21 @@ local quote_override = { local function toggle_function(node) local struct = helpers.destructure_node(node) - if struct.body:match("\n") then + if type(struct.body) == "table" then return end if helpers.node_is_multiline(node) then - return "function" .. struct.parameters .. " " .. struct.body .. " end" + local body = struct.body and (struct.body .. " ") or "" + return "function" .. struct.parameters .. " " .. body .. "end" else - return { "function" .. struct.parameters, struct.body, "end" }, { format = true, cursor = {} } + return { "function" .. struct.parameters, struct.body or "", "end" }, { format = true, cursor = {} } end end local function toggle_named_function(node) local struct = helpers.destructure_node(node) - if struct.body:match("\n") then + if type(struct.body) == "table" then return end diff --git a/lua/ts-node-action/filetypes/python.lua b/lua/ts-node-action/filetypes/python.lua index fbb01c8..53e1c81 100644 --- a/lua/ts-node-action/filetypes/python.lua +++ b/lua/ts-node-action/filetypes/python.lua @@ -16,7 +16,7 @@ local actions = require("ts-node-action.actions") -- a normal case. For unary, we don't want any padding, and generally (always?) -- it is preceded by a named node. When padding, we see these as -- prev_text=nil, so we can use that to detect the unary case with a special --- key "nil", to represent it. +-- key "prev_nil", to represent it. local padding = { [","] = "%s ", [":"] = "%s ", @@ -28,8 +28,8 @@ local padding = { ["and"] = " %s ", ["or"] = " %s ", ["is"] = " %s ", - ["not"] = { [""] = " %s ", ["is"] = "%s " }, - ["in"] = { [""] = " %s ", ["not"] = "%s " }, + ["not"] = { " %s ", ["is"] = "%s " }, + ["in"] = { " %s ", ["not"] = "%s " }, ["=="] = " %s ", ["!="] = " %s ", [">="] = " %s ", @@ -37,7 +37,7 @@ local padding = { [">"] = " %s ", ["<"] = " %s ", ["+"] = " %s ", - ["-"] = { [""] = " %s ", ["nil"] = "%s", }, + ["-"] = { " %s ", ["prev_nil"] = "%s", }, ["*"] = " %s ", ["/"] = " %s ", ["//"] = " %s ", @@ -58,10 +58,7 @@ local boolean_override = { --- @param node TSNode local function node_trim_whitespace(node) local start_row, _, end_row, _ = node:range() - vim.cmd( - "silent! keeppatterns " .. (start_row + 1) .. "," .. (end_row + 1) .. - "s/\\s\\+$//g" - ) + vim.cmd("silent! keeppatterns " .. (start_row + 1) .. "," .. (end_row + 1) .. "s/\\s\\+$//g") end -- When inlined, these nodes must be parenthesized to avoid changing the @@ -82,10 +79,10 @@ local node_types_to_parenthesize = { } local function parenthesize_if_needed(node, text) - if node_types_to_parenthesize[node:type()] and - text:sub(1, 1) ~= "(" then + if node_types_to_parenthesize[node:type()] and text:sub(1, 1) ~= "(" then return "(" .. text .. ")" end + return text end @@ -104,8 +101,10 @@ local function collapse_child_nodes(padding_override) if not helpers.node_is_multiline(node) then return helpers.node_text(node) end + local tbl = actions.toggle_multiline(padding_override) local replacement = tbl[1][1](node) + return replacement end @@ -123,10 +122,10 @@ end --- @param node TSNode --- @return string|nil, string|nil, string local function node_text_lhs_rhs(node, padding_override) - local lhs = nil - local rhs = nil - local type = node:type() - local child = node:named_child(0) + local lhs = nil + local rhs = nil + local type = node:type() + local child = node:named_child(0) local collapse = collapse_child_nodes(padding_override) if type == "return_statement" then @@ -147,10 +146,10 @@ local function node_text_lhs_rhs(node, padding_override) rhs = collapse(child) elseif type == "call" then local identifier = helpers.node_text(child:named_child(0)) - child = child:named_child(1) - rhs = identifier .. collapse(child) + child = child:named_child(1) + rhs = identifier .. collapse(child) elseif type == "boolean_operator" or - type == "parenthesized_expression" then + type == "parenthesized_expression" then rhs = collapse(child) end @@ -177,8 +176,8 @@ end local function find_row_parent(parent, parent_type, start_row) while parent ~= nil and - parent_type ~= "if_statement" and - parent_type ~= "for_statement" do + parent_type ~= "if_statement" and + parent_type ~= "for_statement" do parent = parent:parent() if parent == nil then return nil @@ -212,7 +211,7 @@ local function skip_parens_by_reparenting(parent, parent_type) local paren_parent = parent:parent() local paren_parent_type = paren_parent:type() if paren_parent_type == "assignment" or - paren_parent_type == "return_statement" then + paren_parent_type == "return_statement" then parent = paren_parent parent_type = paren_parent_type end @@ -252,7 +251,6 @@ local function collect_named_children(parent, children, comments) end end - --- @param if_statement TSNode --- @return table local function destructure_if_statement(if_statement) @@ -337,18 +335,18 @@ local function expand_cond_expr(stmt, padding_override) end local start_row, start_col = parent:start() - local row_parent = find_row_parent(parent, parent_type, start_row) - local cursor = {} + local row_parent = find_row_parent(parent, parent_type, start_row) + local cursor = {} -- when we are embedded on the end of an inlined if/for statement, we need -- to expand on to the next line and shift the cursor/indent - local if_indent = "" - local else_indent = "" + local if_indent = "" + local else_indent = "" if row_parent then local _, row_start_col = row_parent:start() -- cursor position is relative to the node being replaced (parent) - cursor = { row = 1, col = row_start_col - start_col + 4 } - if_indent = string.rep(" ", row_start_col + 4) - else_indent = if_indent + cursor = { row = 1, col = row_start_col - start_col + 4 } + if_indent = string.rep(" ", row_start_col + 4) + else_indent = if_indent else else_indent = string.rep(" ", start_col) end @@ -422,7 +420,7 @@ local function body_types_are_inlineable(cons_type, alt_type, cons_lhs, alt_lhs) ["parenthesized_expression"] = true, } return mixable_match_body_types[cons_type] and - mixable_match_body_types[alt_type] + mixable_match_body_types[alt_type] end --- @param stmt table { node, condition, consequence, alternative, comments } @@ -444,17 +442,17 @@ local function inline_ifelse(stmt, padding_override) stmt.alternative[1], padding_override ) - if alt_rhs == nil or - not body_types_are_inlineable(cons_type, alt_type, cons_lhs, alt_lhs) then + if alt_rhs == nil or not body_types_are_inlineable(cons_type, alt_type, cons_lhs, alt_lhs) then return end + alt_rhs = parenthesize_if_needed(alt_child, alt_rhs) local cond_text = collapse_child_nodes(padding_override)(stmt.condition) local replacement = cons_lhs .. cons_rhs .. - " if " .. cond_text .. - " else " .. alt_rhs + " if " .. cond_text .. + " else " .. alt_rhs return replacement, { cursor = { col = string.len(cons_lhs .. cons_rhs) + 1 }, @@ -462,7 +460,7 @@ local function inline_ifelse(stmt, padding_override) end --- @param padding_override table ---- @return function +--- @return table local function inline_if_statement(padding_override) padding_override = padding_override or padding @@ -474,6 +472,7 @@ local function inline_if_statement(padding_override) if #stmt.consequence > 1 or #stmt.alternative > 1 then return end + if #stmt.comments > 0 then return end @@ -498,7 +497,7 @@ local function inline_if_statement(padding_override) end --- @param padding_override table ---- @return function +--- @return table|nil local function expand_conditional_expression(padding_override) padding_override = padding_override or padding @@ -506,9 +505,8 @@ local function expand_conditional_expression(padding_override) --- @return string, table, TSNode local function action(conditional_expression) local stmt = destructure_conditional_expression(conditional_expression) - if #stmt.comments > 0 then - return - end + if #stmt.comments > 0 then return end + return expand_cond_expr(stmt, padding_override) end diff --git a/lua/ts-node-action/filetypes/ruby.lua b/lua/ts-node-action/filetypes/ruby.lua index eeebd35..2167125 100644 --- a/lua/ts-node-action/filetypes/ruby.lua +++ b/lua/ts-node-action/filetypes/ruby.lua @@ -3,7 +3,7 @@ local actions = require("ts-node-action.actions") local padding = { [","] = "%s ", - [":"] = "%s ", + [":"] = { "%s ", ["next_nil"] = "%s" }, ["{"] = "%s ", ["=>"] = " %s ", ["="] = " %s ", @@ -16,16 +16,17 @@ local padding = { local identifier_formats = { "snake_case", "pascal_case", "screaming_snake_case" } +local uncollapsible = { + ["conditional"] = true +} + local function toggle_block(node) local structure = helpers.destructure_node(node) + if type(structure.body) == "table" then return end + local replacement if helpers.node_is_multiline(node) then - if string.find(structure.body, "\n") then - print("(TS:Action) Cannot collapse multi-line block") - return - end - if structure.parameters then replacement = "{ " .. structure.parameters .. " " .. structure.body .. " }" else @@ -43,7 +44,7 @@ local function toggle_block(node) end local function inline_conditional(structure) - if structure.consequence:match("\n") then + if type(structure.consequence) == "table" then return end @@ -62,7 +63,7 @@ local function collapse_ternary(structure) " ? ", structure.consequence, " : ", - vim.trim(string.gsub(structure.alternative, "else\n", "")) + structure.alternative[2] } return table.concat(replacement), { cursor = { col = #replacement[1] + 1 } } @@ -130,10 +131,10 @@ return { ["identifier"] = actions.cycle_case(identifier_formats), ["constant"] = actions.cycle_case(identifier_formats), ["binary"] = actions.toggle_operator(), - ["array"] = actions.toggle_multiline(padding), - ["hash"] = actions.toggle_multiline(padding), - ["argument_list"] = actions.toggle_multiline(padding), - ["method_parameters"] = actions.toggle_multiline(padding), + ["array"] = actions.toggle_multiline(padding, uncollapsible), + ["hash"] = actions.toggle_multiline(padding, uncollapsible), + ["argument_list"] = actions.toggle_multiline(padding, uncollapsible), + ["method_parameters"] = actions.toggle_multiline(padding, uncollapsible), ["integer"] = actions.toggle_int_readability(), ["block"] = { { toggle_block, name = "Toggle Block" } }, ["do_block"] = { { toggle_block, name = "Toggle Block" } }, diff --git a/lua/ts-node-action/helpers.lua b/lua/ts-node-action/helpers.lua index 2ddd142..e07a9dc 100644 --- a/lua/ts-node-action/helpers.lua +++ b/lua/ts-node-action/helpers.lua @@ -1,11 +1,18 @@ local M = {} --- Returns node text as a string +-- Returns node text as a string if single-line, or table if multi-line -- --- @param node TSNode ---- @return string +--- @return table|string|nil function M.node_text(node) - return vim.treesitter.query.get_node_text(node, vim.api.nvim_get_current_buf()) + if not node then return end + + local text = vim.trim(vim.treesitter.query.get_node_text(node, 0)) + if text:match("\n") then + return vim.tbl_map(vim.trim, vim.split(text, "\n")) + else + return text + end end -- Determine if a node spans multiple lines @@ -30,43 +37,44 @@ end -- { -- ["is"] = " %s ", -- ["not"] = { --- [""] = " %s ", +-- " %s ", -- ["is"] = "%s ", -- }, -- } -- The ["is"] key under "not" overrides the format to remove the space when the -- previous text is "is". --- A [""] key is a catch-all for any non-nil prev_text. --- A ["nil"] key will match when prev_text == nil. --- See filetypes/python.lua for more info. +-- A ["prev_nil"] key will match when there is no previous node text +-- A ["next_nil"] key will match when there is no next node text +-- If none of the context_prev's apply, the string in index 1 will be used +-- See filetypes/python.lua or filetypes/ruby.lua for more examples -- --- @param node TSNode --- @param padding table ---- @param context string|nil The [presumed padded] text of the previous node. ---- @return string -function M.padded_node_text(node, padding, context) - local text = M.node_text(node) - - if padding[text] then - local format = padding[text] +--- @return string|table|nil +function M.padded_node_text(node, padding) + local text = M.node_text(node) + local format = padding[text] - if type(format) == "table" then - context = context and vim.trim(context) + if not format then return text end - if format[context] then - text = string.format(format[context], text) - elseif format["nil"] and context == nil then - text = string.format(format["nil"], text) - elseif format[""] then - text = string.format(format[""], text) - end + if type(format) == "table" then + local context_prev = M.node_text(node:prev_sibling()) + local context_next = M.node_text(node:next_sibling()) + if format[context_prev] then + format = format[context_prev] + elseif not context_prev and format["prev_nil"] then + format = format["prev_nil"] + elseif format[context_next] then + format = format[context_next] + elseif not context_next and format["next_nil"] then + format = format["next_nil"] else - text = string.format(format, text) + format = format[1] end end - return text + return string.format(format, text) end -- Prints out a node's tree, showing each child's index, type, text, and ID @@ -84,14 +92,14 @@ function M.debug_print_tree(node) vim.pretty_print(tree) end --- Dissassembles a node tree into it's named and unnamed parts +-- Disassembles a node tree into it's named and unnamed parts -- --- @param node TSNode --- @return table function M.destructure_node(node) local structure = {} for child, id in node:iter_children() do - structure[id or child:type()] = vim.trim(M.node_text(child)) + structure[id or child:type()] = M.node_text(child) end return structure diff --git a/spec/filetypes/lua_spec.lua b/spec/filetypes/lua_spec.lua index cabfdab..a479fca 100644 --- a/spec/filetypes/lua_spec.lua +++ b/spec/filetypes/lua_spec.lua @@ -99,8 +99,31 @@ describe("function_definition (anon)", function() assert.are.same(text, Helper:call(text, { 1, 11 })) end) -end) + it("expands single-line function to multi-line (no-body)", function() + assert.are.same( + { + "local a = function(a, b, c)", + "", + "end" + }, + Helper:call({ "local a = function(a, b, c) end" }, { 1, 11 }) + ) + end) + + it("collapses multi-line function to single-line (no-body)", function() + assert.are.same( + { + "local a = function(a, b, c) end" + }, + Helper:call({ + "local a = function(a, b, c)", + "", + "end" + }, { 1, 11 }) + ) + end) +end) describe("function_declaration (named)", function() it("collapses multi-line function to single line", function() diff --git a/spec/filetypes/python/conditional_padding_spec.lua b/spec/filetypes/python/conditional_padding_spec.lua new file mode 100644 index 0000000..a60b0eb --- /dev/null +++ b/spec/filetypes/python/conditional_padding_spec.lua @@ -0,0 +1,43 @@ +dofile("./spec/spec_helper.lua") + +local Helper = SpecHelper.new("python", { shiftwidth = 4 }) + +describe("conditional padding", function() + it("checking 'is not'", function() + assert.are.same( + { + [[x = 1 if y is not None and foo() > 100 else 2]] + }, + Helper:call({ + 'if y is not None and foo() > 100:', + ' x = 1', + 'else:', + ' x = 2', + }) + ) + end) + + it("checking unary and binary '-' operator", function() + assert.are.same( + { "xs = [x for x in range(10) if x + -3 or -x and x - 3 == 0 and abs(x - 1) < 2]" }, + Helper:call({ + "xs = [", + " x", + " for x in range(10)", + " if x + -3 or -x and x - 3 == 0 and abs(x - 1) < 2", + "]" + }, { 1, 6 }) + ) + end) + + it("checking 'not in'", function() + assert.are.same( + { "print(5 not in list1)" }, + Helper:call({ + "print(", + " 5 not in list1", + ")" + }, { 1, 6 }) + ) + end) +end) diff --git a/spec/filetypes/ruby_spec.lua b/spec/filetypes/ruby_spec.lua index 4a271b6..8215fc5 100644 --- a/spec/filetypes/ruby_spec.lua +++ b/spec/filetypes/ruby_spec.lua @@ -30,16 +30,18 @@ describe("if", function() ) end) - pending("inlines to ternary statement", function() + it("inlines to ternary statement", function() assert.are.same( - { [[greet? ? puts("hello", "goodbye") : puts("booooo", "you lack creativity", "tosser")]] }, - Helper:call({ - [[if greet?]], - [[ puts "hello", "goodbye"]], - [[else]], - [[ puts "booooo", "you lack creativity", "tosser"]], - [[end]], - }) + { [[greet? ? puts("hello") : puts("booooo")]] }, + Helper:call( + { + [[if greet?]], + [[ puts("hello")]], + [[else]], + [[ puts("booooo")]], + [[end]], + } + ) ) end) end) @@ -420,17 +422,396 @@ describe("pair", function() end) describe("argument_list", function() + it("expands one positional arg", function() + assert.are.same( + { + "call(", + " a", + ")" + }, + Helper:call({ "call(a)" }, { 1, 5 }) + ) + end) + + it("expands multiple positional args", function() + assert.are.same( + { + "call(", + " a,", + " b", + ")" + }, + Helper:call({ "call(a, b)" }, { 1, 5 }) + ) + end) + + it("expands one keyword arg with explicit value", function() + assert.are.same( + { + "call(", + [[ arg: "something"]], + ")" + }, + Helper:call({ [[call(arg: "something")]] }, { 1, 5 }) + ) + end) + + it("expands keyword arg with implicit value", function() + assert.are.same( + { + "call(", + " arg:", + ")" + }, + Helper:call({ [[call(arg:)]] }, { 1, 5 }) + ) + end) + + it("expands with passed block", function() + assert.are.same( + { + "call(", + " &block", + ")" + }, + Helper:call({ [[call(&block)]] }, { 1, 5 }) + ) + end) + + it("expands with provided block", function() + assert.are.same( + { + "call(", + " arg", + ") { |n| puts n }" + }, + Helper:call({ [[call(arg) { |n| puts n }]] }, { 1, 5 }) + ) + end) + + it("expands keyword arg with expression value", function() + assert.are.same( + { + "call(", + " arg: count? ? 1 : 2", + ")" + }, + Helper:call({ + [[call(arg: count? ? 1 : 2)]] + }, { 1, 5 }) + ) + end) + + it("expands positional arg with expression value", function() + assert.are.same( + { + "call(", + " count? ? 1 : 2", + ")" + }, + Helper:call({ + [[call(count? ? 1 : 2)]] + }, { 1, 5 }) + ) + end) + + it("expands with a mix of all", function() + assert.are.same( + { + "call(", + " a,", + " b,", + " arg: true,", + " arg2:,", + " &blk", + ")" + }, + Helper:call({ [[call(a, b, arg: true, arg2:, &blk)]] }, { 1, 5 }) + ) + end) + + it("collapses one positional arg", function() + assert.are.same( + { "call(a)" }, + Helper:call({ + "call(", + " a", + ")" + }, { 1, 5 }) + ) + end) + + it("collapses multiple positional args", function() + assert.are.same( + { "call(a, b)" }, + Helper:call({ + "call(", + " a,", + " b", + ")" + }, { 1, 5 }) + ) + end) + it("collapses one keyword arg with explicit value", function() + assert.are.same( + { [[call(arg: "something")]] }, + Helper:call({ + "call(", + [[ arg: "something"]], + ")" + }, { 1, 5 }) + ) + end) + + it("collapses keyword arg with implicit value", function() + assert.are.same( + { [[call(arg:)]] }, + Helper:call({ + "call(", + " arg:", + ")" + }, { 1, 5 }) + ) + end) + + it("collapses keyword arg with expression value", function() + assert.are.same( + { [[call(arg: count? ? 1 : 2)]] }, + Helper:call({ + "call(", + " arg: count? ? 1 : 2", + ")" + }, { 1, 5 }) + ) + end) + + it("collapses with passed block", function() + assert.are.same( + { [[call(&block)]] }, + Helper:call({ + "call(", + " &block", + ")" + }, { 1, 5 }) + ) + end) + + it("collapses with provided block", function() + assert.are.same( + { [[call(arg) { |n| puts n }]] }, + Helper:call({ + "call(", + " arg", + ") { |n| puts n }" + }, { 1, 5 }) + ) + end) + + it("collapses with a mix of all", function() + assert.are.same( + { [[call(a, b, arg: true, arg2:, &blk)]] }, + Helper:call({ + "call(", + " a,", + " b,", + " arg: true,", + " arg2:,", + " &blk", + ")" + }, { 1, 5 }) + ) + end) end) describe("method_parameters", function() + it("expands positional argument", function() + assert.are.same({ + "def something(", + " a,", + " b,", + " c", + ")", + " puts a + b + c", + "end" + }, + Helper:call({ + "def something(a, b, c)", + " puts a + b + c", + "end" + }, { 1, 14 })) + end) + + it("expands keyword arg", function() + assert.are.same({ + "def something(", + " a:,", + " b:,", + " c:", + ")", + " puts a + b + c", + "end" + }, + Helper:call({ + "def something(a:, b:, c:)", + " puts a + b + c", + "end" + }, { 1, 14 })) + end) + + it("expands block", function() + assert.are.same({ + "def something(", + " &block", + ")", + " puts a + b + c", + "end" + }, + Helper:call({ + "def something(&block)", + " puts a + b + c", + "end" + }, { 1, 14 })) + end) + + it("expands keyword arg with default value", function() + assert.are.same({ + "def something(", + " a: 1,", + " b: 2", + ")", + " puts a + b + c", + "end" + }, + Helper:call({ + "def something(a: 1, b: 2)", + " puts a + b + c", + "end" + }, { 1, 14 })) + end) + + it("expands positional arg with default value", function() + assert.are.same({ + "def something(", + " a = 1,", + " b = 2", + ")", + " puts a + b + c", + "end" + }, + Helper:call({ + "def something(a = 1, b = 2)", + " puts a + b + c", + "end" + }, { 1, 14 })) + end) + + it("collapses positional argument", function() + assert.are.same({ + "def something(a, b, c)", + " puts a + b + c", + "end" + }, + Helper:call({ + "def something(", + " a,", + " b,", + " c", + ")", + " puts a + b + c", + "end" + }, { 1, 14 })) + end) + it("collapses keyword arg", function() + assert.are.same({ + "def something(a:, b:, c:)", + " puts a + b + c", + "end" + }, + Helper:call({ + "def something(", + " a:,", + " b:,", + " c:", + ")", + " puts a + b + c", + "end" + }, { 1, 14 })) + end) + + it("collapses block", function() + assert.are.same({ + "def something(&block)", + " puts a + b + c", + "end" + }, + Helper:call({ + "def something(", + " &block", + ")", + " puts a + b + c", + "end" + }, { 1, 14 })) + end) + + it("collapses keyword arg with default value", function() + assert.are.same({ + "def something(a: 1, b: 2)", + " puts a + b + c", + "end" + }, + Helper:call({ + "def something(", + " a: 1,", + " b: 2", + ")", + " puts a + b + c", + "end" + }, { 1, 14 })) + end) + + it("collapses positional arg with default value", function() + assert.are.same({ + "def something(a = 1, b = 2)", + " puts a + b + c", + "end" + }, + Helper:call({ + "def something(", + " a = 1,", + " b = 2", + ")", + " puts a + b + c", + "end" + }, { 1, 14 })) + end) end) describe("constant", function() + it("transforms pascal case to screaming snake case (multi-word)", function() + assert.are.same({ "TS_NODE_ACTION" }, Helper:call({ "TsNodeAction" })) + end) + + it("transforms screaming snake case to snake case (multi-word)", function() + assert.are.same({ "ts_node_action" }, Helper:call({ "TS_NODE_ACTION" })) + end) + + it("transforms pascal case to screaming snake case (single-word)", function() + assert.are.same({ "NODE" }, Helper:call({ "Node" })) + end) + it("transforms screaming snake case to snake case (single-word)", function() + assert.are.same({ "node" }, Helper:call({ "NODE" })) + end) end) describe("identifier", function() + it("transforms snake case to pascal case (multi-word)", function() + assert.are.same({ "TsNodeAction" }, Helper:call({ "ts_node_action" })) + end) + it("transforms snake case to pascal case (single-word)", function() + assert.are.same({ "Node" }, Helper:call({ "node" })) + end) end) diff --git a/spec/spec_helper.lua b/spec/spec_helper.lua index f198732..3073c7b 100644 --- a/spec/spec_helper.lua +++ b/spec/spec_helper.lua @@ -3,6 +3,8 @@ local Buffer = {} --- @class Buffer --- @field lang string The language for this buffer --- @field opts table Buffer local settings +--- @field setup self +--- @field teardown nil --- @param lang string --- @param buf_opts table @@ -25,7 +27,7 @@ function Buffer.new(lang, buf_opts) return instance end ---- @return Buffer +--- @return self function Buffer:setup() self.handle = vim.api.nvim_create_buf(false, true) vim.treesitter.start(self.handle, self.lang) @@ -39,7 +41,7 @@ end -- Fakes cursor location by just returning the node at where the cursor should be --- @param pos table 1-indexed { row, col } ---- @return Buffer +--- @return self function Buffer:set_cursor(pos) local row = pos[1] - 1 local col = pos[2] - 1 @@ -56,7 +58,7 @@ function Buffer:set_cursor(pos) end --- @param text string|table ---- @return Buffer +--- @return self function Buffer:write(text) if type(text) ~= "table" then text = { text } @@ -76,7 +78,7 @@ function Buffer:teardown() vim.api.nvim_buf_delete(self.handle, { force = true }) end ---- @return Buffer +--- @return self function Buffer:run_action() vim.api.nvim_buf_call(self.handle, require("ts-node-action").node_action) return self @@ -89,7 +91,7 @@ _G.SpecHelper = SpecHelper --- @class SpecHelper A general wrapper, available in the global scope, for test related helpers --- @field lang string The language for this buffer --- @field buf_opts table Buffer local settings ---- @field call function +--- @field call table --- @param lang string --- @param buf_opts table|nil