From 9d6926d73e6f2e99166d6ef96d0361c544eef1a0 Mon Sep 17 00:00:00 2001 From: Artem Medeu Date: Sun, 1 Oct 2023 12:17:33 +0600 Subject: [PATCH] feat(#37): head tail movement support + deprecate previous API's --- README.md | 17 ++++- lua/nvim-paredit/api/init.lua | 12 +++- lua/nvim-paredit/api/motions.lua | 43 ++++++++---- lua/nvim-paredit/defaults.lua | 16 ++++- lua/nvim-paredit/utils/common.lua | 7 ++ tests/nvim-paredit/motion_spec.lua | 103 +++++++++++++++++++++-------- 6 files changed, 151 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 515b577..62cdd1e 100644 --- a/README.md +++ b/README.md @@ -91,18 +91,31 @@ paredit.setup({ ["O"] = { paredit.api.raise_element, "Raise element" }, ["E"] = { - paredit.api.move_to_next_element, + paredit.api.move_to_next_element_tail, "Jump to next element tail", -- by default all keybindings are dot repeatable repeatable = false, mode = { "n", "x", "o", "v" }, }, + ["W"] = { + paredit.api.move_to_next_element_head, + "Jump to next element head", + repeatable = false, + mode = { "n", "x", "o", "v" }, + }, + ["B"] = { - paredit.api.move_to_prev_element, + paredit.api.move_to_prev_element_head, "Jump to previous element head", repeatable = false, mode = { "n", "x", "o", "v" }, }, + ["gE"] = { + paredit.api.move_to_prev_element_tail, + "Jump to previous element tail", + repeatable = false, + mode = { "n", "x", "o", "v" }, + }, -- These are text object selection keybindings which can used with standard `d, y, c`, `v` ["af"] = { diff --git a/lua/nvim-paredit/api/init.lua b/lua/nvim-paredit/api/init.lua index ea655c5..66c2974 100644 --- a/lua/nvim-paredit/api/init.lua +++ b/lua/nvim-paredit/api/init.lua @@ -5,6 +5,7 @@ local raising = require("nvim-paredit.api.raising") local motions = require("nvim-paredit.api.motions") local selections = require("nvim-paredit.api.selections") local deletions = require("nvim-paredit.api.deletions") +local common = require("nvim-paredit.utils.common") local M = { slurp_forwards = slurping.slurp_forwards, @@ -20,8 +21,15 @@ local M = { raise_form = raising.raise_form, raise_element = raising.raise_element, - move_to_next_element = motions.move_to_next_element, - move_to_prev_element = motions.move_to_prev_element, + -- TODO: remove deprecated code in next versions + move_to_next_element = common.deprecate(motions.move_to_next_element_tail, "use `api.move_to_next_element_tail`"), + move_to_next_element_tail = motions.move_to_next_element_tail, + move_to_next_element_head = motions.move_to_next_element_head, + + -- TODO: remove deprecated code in next versions + move_to_prev_element = common.deprecate(motions.move_to_prev_element_head, "use `api.move_to_prev_element_head`"), + move_to_prev_element_head = motions.move_to_prev_element_head, + move_to_prev_element_tail = motions.move_to_prev_element_tail, select_around_form = selections.select_around_form, select_in_form = selections.select_in_form, diff --git a/lua/nvim-paredit/api/motions.lua b/lua/nvim-paredit/api/motions.lua index a72fab1..f08bf29 100644 --- a/lua/nvim-paredit/api/motions.lua +++ b/lua/nvim-paredit/api/motions.lua @@ -52,7 +52,8 @@ local function get_next_node_from_cursor(lang, reversed) end end -function M._move_to_element(count, reversed) +function M._move_to_element(count, reversed, is_head) + is_head = is_head or false local lang = langs.get_language_api() local current_node = get_next_node_from_cursor(lang, reversed) @@ -67,16 +68,20 @@ function M._move_to_element(count, reversed) local is_in_middle = false if reversed then - node_edge = { current_node:start() } traversal_fn = traversal.get_prev_sibling_ignoring_comments - if common.compare_positions(cursor_pos, node_edge) == 1 then - is_in_middle = true + if is_head then + node_edge = { current_node:start() } + if common.compare_positions(cursor_pos, node_edge) == 1 then + is_in_middle = true + end end else - node_edge = { current_node:end_() } traversal_fn = traversal.get_next_sibling_ignoring_comments - if common.compare_positions({ node_edge[1], node_edge[2] - 1 }, cursor_pos) == 1 then - is_in_middle = true + if not is_head then + node_edge = { current_node:end_() } + if common.compare_positions({ node_edge[1], node_edge[2] - 1 }, cursor_pos) == 1 then + is_in_middle = true + end end end @@ -95,7 +100,7 @@ function M._move_to_element(count, reversed) count = count, }) if sibling then - if reversed then + if is_head then next_pos = { sibling:start() } else next_pos = { sibling:end_() } @@ -107,7 +112,7 @@ function M._move_to_element(count, reversed) return end - if reversed then + if is_head then cursor_pos = { next_pos[1] + 1, next_pos[2] } else cursor_pos = { next_pos[1] + 1, next_pos[2] - 1 } @@ -125,16 +130,28 @@ local function ensure_visual_if_operator_pending() end end -function M.move_to_prev_element() +function M.move_to_prev_element_head() + local count = vim.v.count1 + ensure_visual_if_operator_pending() + M._move_to_element(count, true, true) +end + +function M.move_to_prev_element_tail() + local count = vim.v.count1 + ensure_visual_if_operator_pending() + M._move_to_element(count, true, false) +end + +function M.move_to_next_element_tail() local count = vim.v.count1 ensure_visual_if_operator_pending() - M._move_to_element(count, true) + M._move_to_element(count, false, false) end -function M.move_to_next_element() +function M.move_to_next_element_head() local count = vim.v.count1 ensure_visual_if_operator_pending() - M._move_to_element(count, false) + M._move_to_element(count, false, true) end return M diff --git a/lua/nvim-paredit/defaults.lua b/lua/nvim-paredit/defaults.lua index 55f3af8..00c5b91 100644 --- a/lua/nvim-paredit/defaults.lua +++ b/lua/nvim-paredit/defaults.lua @@ -19,17 +19,29 @@ M.default_keys = { ["O"] = { api.raise_element, "Raise element" }, ["E"] = { - api.move_to_next_element, + api.move_to_next_element_tail, "Next element tail", repeatable = false, mode = { "n", "x", "o", "v" }, }, + ["W"] = { + api.move_to_next_element_head, + "Next element head", + repeatable = false, + mode = { "n", "x", "o", "v" }, + }, ["B"] = { - api.move_to_prev_element, + api.move_to_prev_element_head, "Previous element head", repeatable = false, mode = { "n", "x", "o", "v" }, }, + ["gE"] = { + api.move_to_prev_element_tail, + "Previous element tail", + repeatable = false, + mode = { "n", "x", "o", "v" }, + }, ["af"] = { api.select_around_form, diff --git a/lua/nvim-paredit/utils/common.lua b/lua/nvim-paredit/utils/common.lua index c4c7fbd..93b7c22 100644 --- a/lua/nvim-paredit/utils/common.lua +++ b/lua/nvim-paredit/utils/common.lua @@ -79,4 +79,11 @@ function M.is_whitespace_under_cursor(lang) or char_under_cursor[1] == "" end +function M.deprecate(fn, message) + return function(...) + print("Warning: Deprecated function called. " .. message) + return fn(...) + end +end + return M diff --git a/tests/nvim-paredit/motion_spec.lua b/tests/nvim-paredit/motion_spec.lua index dbc8826..b822caf 100644 --- a/tests/nvim-paredit/motion_spec.lua +++ b/tests/nvim-paredit/motion_spec.lua @@ -8,67 +8,114 @@ local expect = require("tests.nvim-paredit.utils").expect describe("motions", function() vim.api.nvim_buf_set_option(0, "filetype", "clojure") - it("should jump to next element in form", function() + it("should jump to next element in form (tail)", function() prepare_buffer({ content = "(aa (bb) @(cc) #{1})", cursor = { 1, 2 }, }) - paredit.move_to_next_element() + paredit.move_to_next_element_tail() expect({ cursor = { 1, 7 }, }) - paredit.move_to_next_element() + paredit.move_to_next_element_tail() expect({ cursor = { 1, 13 }, }) - paredit.move_to_next_element() + paredit.move_to_next_element_tail() expect({ cursor = { 1, 18 }, }) - paredit.move_to_next_element() + paredit.move_to_next_element_tail() expect({ cursor = { 1, 18 }, }) end) - it("should jump to previous element in form", function() + it("should jump to next element in form (head)", function() + prepare_buffer({ + content = "(aa (bb) @(cc) #{1})", + cursor = { 1, 2 }, + }) + + paredit.move_to_next_element_head() + expect({ + cursor = { 1, 4 }, + }) + + paredit.move_to_next_element_head() + expect({ + cursor = { 1, 9 }, + }) + + paredit.move_to_next_element_head() + expect({ + cursor = { 1, 15 }, + }) + + paredit.move_to_next_element_head() + expect({ + cursor = { 1, 15 }, + }) + end) + + it("should jump to previous element in form (head)", function() prepare_buffer({ content = "(aa (bb) '(cc))", cursor = { 1, 9 }, }) - paredit.move_to_prev_element() + paredit.move_to_prev_element_head() expect({ cursor = { 1, 4 }, }) - paredit.move_to_prev_element() + paredit.move_to_prev_element_head() expect({ cursor = { 1, 1 }, }) - paredit.move_to_prev_element() + paredit.move_to_prev_element_head() expect({ cursor = { 1, 1 }, }) end) + it("should jump to previous element in form (tail)", function() + prepare_buffer({ + content = "(aa (bb) '(cc))", + cursor = { 1, 9 }, + }) + + paredit.move_to_prev_element_tail() + expect({ + cursor = { 1, 7 }, + }) + paredit.move_to_prev_element_tail() + expect({ + cursor = { 1, 2 }, + }) + paredit.move_to_prev_element_tail() + expect({ + cursor = { 1, 2 }, + }) + end) + it("should skip comments", function() prepare_buffer({ content = { "(aa", ";; comment", "bb)" }, cursor = { 1, 2 }, }) - paredit.move_to_next_element() + paredit.move_to_next_element_tail() expect({ cursor = { 3, 1 }, }) - paredit.move_to_prev_element() + paredit.move_to_prev_element_head() expect({ cursor = { 3, 0 }, }) - paredit.move_to_prev_element() + paredit.move_to_prev_element_head() expect({ cursor = { 1, 1 }, }) @@ -79,15 +126,15 @@ describe("motions", function() content = { "(aa", ";; comment", "bb)" }, cursor = { 2, 3 }, }) - paredit.move_to_next_element() + paredit.move_to_next_element_tail() expect({ cursor = { 3, 1 }, }) - paredit.move_to_prev_element() + paredit.move_to_prev_element_head() expect({ cursor = { 3, 0 }, }) - paredit.move_to_prev_element() + paredit.move_to_prev_element_head() expect({ cursor = { 1, 1 }, }) @@ -95,14 +142,14 @@ describe("motions", function() content = { "(aa", ";; comment", "bb)" }, cursor = { 2, 3 }, }) - paredit.move_to_prev_element() + paredit.move_to_prev_element_head() expect({ cursor = { 1, 1 }, }) end) it("should move to the end of the current form before jumping to next", function() - expect_all(paredit.move_to_next_element, { + expect_all(paredit.move_to_next_element_tail, { { "same line", before_content = "(aaa bbb)", @@ -119,7 +166,7 @@ describe("motions", function() end) it("should move to the start of the current form before jumping to previous", function() - expect_all(paredit.move_to_prev_element, { + expect_all(paredit.move_to_prev_element_head, { { "same line", before_content = "(aaa bbb)", @@ -142,42 +189,42 @@ describe("motions", function() before_content = "( bb)", before_cursor = { 1, 1 }, after_cursor = { 1, 3 }, - action = paredit.move_to_next_element, + action = paredit.move_to_next_element_tail, }, { "forwards skipping comments", before_content = { "( ;; comment", "bb)" }, before_cursor = { 1, 1 }, after_cursor = { 2, 1 }, - action = paredit.move_to_next_element, + action = paredit.move_to_next_element_tail, }, { "forwards from no char", before_content = { "(bb", "", "cc)" }, before_cursor = { 2, 0 }, after_cursor = { 3, 1 }, - action = paredit.move_to_next_element, + action = paredit.move_to_next_element_tail, }, { "backwards", before_content = "(aa) (bb) ", before_cursor = { 1, 9 }, after_cursor = { 1, 5 }, - action = paredit.move_to_prev_element, + action = paredit.move_to_prev_element_head, }, { "backwards skipping comments", before_content = { "(aa ;; comment", " )" }, before_cursor = { 2, 0 }, after_cursor = { 1, 1 }, - action = paredit.move_to_prev_element, + action = paredit.move_to_prev_element_head, }, { "backwards from no char", before_content = { "(bb", "", "cc)" }, before_cursor = { 2, 0 }, after_cursor = { 1, 1 }, - action = paredit.move_to_prev_element, + action = paredit.move_to_prev_element_head, }, }) end) @@ -188,22 +235,22 @@ describe("motions", function() cursor = { 1, 2 }, }) - internal_api._move_to_element(2, false) + internal_api._move_to_element(2, false, false) expect({ cursor = { 1, 13 }, }) - internal_api.move_to_next_element() + internal_api.move_to_next_element_tail() expect({ cursor = { 1, 18 }, }) - internal_api.move_to_next_element() + internal_api.move_to_next_element_tail() expect({ cursor = { 1, 18 }, }) - internal_api._move_to_element(3, true) + internal_api._move_to_element(3, true, true) expect({ cursor = { 1, 4 }, })