diff --git a/README.md b/README.md index 1c8fe5f..c97b7e7 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,9 @@ paredit.api.slurp_forwards() - **`drag_form_backwards`** - **`raise_element`** - **`raise_form`** +- **`delete_form`** +- **`delete_in_form`** +- **`delete_element`** - **`move_to_next_element`** - **`move_to_prev_element`** diff --git a/lua/nvim-paredit/api/deletions.lua b/lua/nvim-paredit/api/deletions.lua new file mode 100644 index 0000000..d91aa64 --- /dev/null +++ b/lua/nvim-paredit/api/deletions.lua @@ -0,0 +1,74 @@ +local traversal = require("nvim-paredit.utils.traversal") +local ts = require("nvim-treesitter.ts_utils") +local langs = require("nvim-paredit.lang") + +local M = {} + +function M.delete_form() + local lang = langs.get_language_api() + local current_form = traversal.find_nearest_form(ts.get_node_at_cursor(), { + lang = lang, + use_source = false, + }) + if not current_form then + return + end + + local root = lang.get_node_root(current_form) + local range = { root:range() } + + local buf = vim.api.nvim_get_current_buf() + -- stylua: ignore + vim.api.nvim_buf_set_text( + buf, + range[1], range[2], + range[3], range[4], + {} + ) +end + +function M.delete_in_form() + local lang = langs.get_language_api() + local current_form = traversal.find_nearest_form(ts.get_node_at_cursor(), { + lang = lang, + use_source = false, + }) + if not current_form then + return + end + + local edges = lang.get_form_edges(current_form) + + local buf = vim.api.nvim_get_current_buf() + -- stylua: ignore + vim.api.nvim_buf_set_text( + buf, + edges.left.range[3], edges.left.range[4], + edges.right.range[1], edges.right.range[2], + {} + ) + + vim.api.nvim_win_set_cursor(0, { edges.left.range[3] + 1, edges.left.range[4] }) +end + +function M.delete_element() + local lang = langs.get_language_api() + local node = ts.get_node_at_cursor() + if not node then + return + end + + local root = lang.get_node_root(node) + local range = { root:range() } + + local buf = vim.api.nvim_get_current_buf() + -- stylua: ignore + vim.api.nvim_buf_set_text( + buf, + range[1], range[2], + range[3], range[4], + {} + ) +end + +return M diff --git a/lua/nvim-paredit/api/init.lua b/lua/nvim-paredit/api/init.lua index 4287812..761de9e 100644 --- a/lua/nvim-paredit/api/init.lua +++ b/lua/nvim-paredit/api/init.lua @@ -3,6 +3,7 @@ local barfing = require("nvim-paredit.api.barfing") local dragging = require("nvim-paredit.api.dragging") local raising = require("nvim-paredit.api.raising") local motions = require("nvim-paredit.api.motions") +local deletions = require("nvim-paredit.api.deletions") local M = { slurp_forwards = slurping.slurp_forwards, @@ -20,6 +21,10 @@ local M = { move_to_next_element = motions.move_to_next_element, move_to_prev_element = motions.move_to_prev_element, + + delete_form = deletions.delete_form, + delete_in_form = deletions.delete_in_form, + delete_element = deletions.delete_element, } return M diff --git a/tests/nvim-paredit/deletions_spec.lua b/tests/nvim-paredit/deletions_spec.lua new file mode 100644 index 0000000..9b7436e --- /dev/null +++ b/tests/nvim-paredit/deletions_spec.lua @@ -0,0 +1,242 @@ +local paredit = require("nvim-paredit.api") + +local prepare_buffer = require("tests.nvim-paredit.utils").prepare_buffer +local expect_all = require("tests.nvim-paredit.utils").expect_all +local expect = require("tests.nvim-paredit.utils").expect + +describe("form deletions", function() + vim.api.nvim_buf_set_option(0, "filetype", "clojure") + + it("should delete the form", function() + prepare_buffer({ + content = "(a)", + cursor = { 1, 1 }, + }) + paredit.delete_form() + expect({ + content = "", + cursor = { 1, 0 }, + }) + end) + + it("should delete a multi line form", function() + prepare_buffer({ + content = { "(a", "b", "c)" }, + cursor = { 1, 1 }, + }) + paredit.delete_form() + expect({ + content = "", + cursor = { 1, 0 }, + }) + end) + + it("should delete a nested form", function() + prepare_buffer({ + content = "(a (a b c))", + cursor = { 1, 5 }, + }) + paredit.delete_form() + expect({ + content = "(a )", + cursor = { 1, 3 }, + }) + end) + + it("should delete different form types", function() + expect_all(paredit.delete_form, { + { + "list", + before_content = "(a)", + before_cursor = { 1, 1 }, + after_content = "", + after_cursor = { 1, 0 }, + }, + { + "vector", + before_content = "[a]", + before_cursor = { 1, 1 }, + after_content = "", + after_cursor = { 1, 0 }, + }, + { + "quoted list", + before_content = "`(a)", + before_cursor = { 1, 2 }, + after_content = "", + after_cursor = { 1, 0 }, + }, + { + "quoted list", + before_content = "'(a)", + before_cursor = { 1, 2 }, + after_content = "", + after_cursor = { 1, 0 }, + }, + { + "anon fn", + before_content = "#(a)", + before_cursor = { 1, 2 }, + after_content = "", + after_cursor = { 1, 0 }, + }, + { + "set", + before_content = "#{a}", + before_cursor = { 1, 2 }, + after_content = "", + after_cursor = { 1, 0 }, + }, + }) + end) +end) + +describe("form inner deletions", function() + vim.api.nvim_buf_set_option(0, "filetype", "clojure") + + it("should delete everything in the form", function() + prepare_buffer({ + content = "(a b)", + cursor = { 1, 2 }, + }) + paredit.delete_in_form() + expect({ + content = "()", + cursor = { 1, 1 }, + }) + end) + + it("should delete everything within a multi line form", function() + prepare_buffer({ + content = { "(a", "b", "c)" }, + cursor = { 2, 0 }, + }) + paredit.delete_in_form() + expect({ + content = "()", + cursor = { 1, 1 }, + }) + end) + + it("should delete everyting within a nested form", function() + prepare_buffer({ + content = "(a (a b c))", + cursor = { 1, 5 }, + }) + paredit.delete_in_form() + expect({ + content = "(a ())", + cursor = { 1, 4 }, + }) + end) + + it("should delete within different form types", function() + expect_all(paredit.delete_in_form, { + { + "list", + before_content = "(a)", + before_cursor = { 1, 1 }, + after_content = "()", + after_cursor = { 1, 1 }, + }, + { + "vector", + before_content = "[a]", + before_cursor = { 1, 1 }, + after_content = "[]", + after_cursor = { 1, 1 }, + }, + { + "quoted list", + before_content = "`(a)", + before_cursor = { 1, 2 }, + after_content = "`()", + after_cursor = { 1, 2 }, + }, + { + "quoted list", + before_content = "'(a)", + before_cursor = { 1, 2 }, + after_content = "'()", + after_cursor = { 1, 2 }, + }, + { + "anon fn", + before_content = "#(a)", + before_cursor = { 1, 2 }, + after_content = "#()", + after_cursor = { 1, 2 }, + }, + { + "set", + before_content = "#{a}", + before_cursor = { 1, 2 }, + after_content = "#{}", + after_cursor = { 1, 2 }, + }, + }) + end) +end) + +describe("element deletions", function() + vim.api.nvim_buf_set_option(0, "filetype", "clojure") + + it("should delete the element under cursor", function() + prepare_buffer({ + content = "(ab cd)", + cursor = { 1, 4 }, + }) + paredit.delete_element() + expect({ + content = "(ab )", + cursor = { 1, 4 }, + }) + end) + + it("should delete different element types", function() + expect_all(paredit.delete_element, { + { + "list", + before_content = "(a)", + before_cursor = { 1, 0 }, + after_content = "", + after_cursor = { 1, 0 }, + }, + { + "vector", + before_content = "[a]", + before_cursor = { 1, 0 }, + after_content = "", + after_cursor = { 1, 0 }, + }, + { + "quoted list", + before_content = "`(a)", + before_cursor = { 1, 0 }, + after_content = "", + after_cursor = { 1, 0 }, + }, + { + "quoted list", + before_content = "'(a)", + before_cursor = { 1, 0 }, + after_content = "", + after_cursor = { 1, 0 }, + }, + { + "anon fn", + before_content = "#(a)", + before_cursor = { 1, 0 }, + after_content = "", + after_cursor = { 1, 0 }, + }, + { + "set", + before_content = "#{a}", + before_cursor = { 1, 0 }, + after_content = "", + after_cursor = { 1, 0 }, + }, + }) + end) +end)