diff --git a/README.md b/README.md index 3e036dc..5547deb 100644 --- a/README.md +++ b/README.md @@ -73,26 +73,28 @@ require("nvim-paredit").setup({ paredit.api.move_to_next_element, "Jump to next element tail", -- by default all keybindings are dot repeatable - repeatable = false + repeatable = false, + mode = { "n", "x", "o", "v" }, }, ["B"] = { paredit.api.move_to_prev_element, "Jump to previous element head", - repeatable = false + repeatable = false, + mode = { "n", "x", "o", "v" }, }, - -- These are text object selection keybindings which can used with standard `d, y, c` + -- These are text object selection keybindings which can used with standard `d, y, c`, `v` ["af"] = { api.select_around_form, "Around form", repeatable = false, - mode = { "o" } + mode = { "o", "v" } }, ["if"] = { api.select_in_form, "In form", repeatable = false, - mode = { "o" } + mode = { "o", "v" } }, } }) diff --git a/lua/nvim-paredit/api/motions.lua b/lua/nvim-paredit/api/motions.lua index 0b72eab..fe1c121 100644 --- a/lua/nvim-paredit/api/motions.lua +++ b/lua/nvim-paredit/api/motions.lua @@ -124,12 +124,25 @@ function M._move_to_element(count, reversed) vim.api.nvim_win_set_cursor(0, cursor_pos) end +-- When in operator-pending mode (`o` or `no`) then we need to switch to +-- visual mode in order for the operator to apply over a range of text. +local function ensure_visual_if_operator_pending() + local mode = vim.api.nvim_get_mode().mode + if mode == "o" or mode == "no" then + common.ensure_visual_mode() + end +end + function M.move_to_prev_element() - M._move_to_element(vim.v.count1, true) + local count = vim.v.count1 + ensure_visual_if_operator_pending() + M._move_to_element(count, true) end function M.move_to_next_element() - M._move_to_element(vim.v.count1, false) + local count = vim.v.count1 + ensure_visual_if_operator_pending() + M._move_to_element(count, false) end return M diff --git a/lua/nvim-paredit/defaults.lua b/lua/nvim-paredit/defaults.lua index cc216e4..ee3944b 100644 --- a/lua/nvim-paredit/defaults.lua +++ b/lua/nvim-paredit/defaults.lua @@ -22,26 +22,26 @@ M.default_keys = { api.move_to_next_element, "Next element tail", repeatable = false, - operator = true, + mode = { "n", "x", "o", "v" }, }, ["B"] = { api.move_to_prev_element, "Previous element head", repeatable = false, - operator = true, + mode = { "n", "x", "o", "v" }, }, ["af"] = { api.select_around_form, "Around form", repeatable = false, - mode = { "o", "v" } + mode = { "o", "v" }, }, ["if"] = { api.select_in_form, "In form", repeatable = false, - mode = { "o", "v" } + mode = { "o", "v" }, }, } diff --git a/lua/nvim-paredit/utils/common.lua b/lua/nvim-paredit/utils/common.lua index 2196796..8e49b13 100644 --- a/lua/nvim-paredit/utils/common.lua +++ b/lua/nvim-paredit/utils/common.lua @@ -59,5 +59,11 @@ function M.intersection(tbl, original) return result end +function M.ensure_visual_mode() + if vim.api.nvim_get_mode().mode ~= "v" then + vim.api.nvim_command("normal! v") + end +end + return M diff --git a/lua/nvim-paredit/utils/keybindings.lua b/lua/nvim-paredit/utils/keybindings.lua index 8adfacf..11e85b3 100644 --- a/lua/nvim-paredit/utils/keybindings.lua +++ b/lua/nvim-paredit/utils/keybindings.lua @@ -17,25 +17,12 @@ function M.with_repeat(fn) end end --- we wrap motion keys with visual mode for operator mode --- such that dE/cE becomes dvE/cvE -function M.visualize(fn) - return function() - vim.api.nvim_command("normal! v") - fn() - end -end - function M.setup_keybindings(opts) for keymap, action in pairs(opts.keys) do local repeatable = true - local operator = false if type(action.repeatable) == "boolean" then repeatable = action.repeatable end - if type(action.operator) == "boolean" then - operator = action.operator - end local fn = action[1] if repeatable then @@ -49,16 +36,6 @@ function M.setup_keybindings(opts) remap = false, silent = true, }) - - if operator then - vim.keymap.set("o", keymap, M.visualize(fn), { - desc = action[2], - buffer = opts.buf or 0, - expr = repeatable, - remap = false, - silent = true, - }) - end end end diff --git a/tests/nvim-paredit/operator_motion_spec.lua b/tests/nvim-paredit/operator_motion_spec.lua index 3e6821a..202e801 100644 --- a/tests/nvim-paredit/operator_motion_spec.lua +++ b/tests/nvim-paredit/operator_motion_spec.lua @@ -2,15 +2,14 @@ local prepare_buffer = require("tests.nvim-paredit.utils").prepare_buffer local feedkeys = require("tests.nvim-paredit.utils").feedkeys local expect = require("tests.nvim-paredit.utils").expect local keybindings = require("nvim-paredit.utils.keybindings") -local motions = require("nvim-paredit.api.motions") -local next_element = keybindings.visualize(motions.move_to_next_element) -local prev_element = keybindings.visualize(motions.move_to_prev_element) +local defaults = require("nvim-paredit.defaults") describe("motions with operator pending", function() before_each(function() - vim.keymap.set("o", "E", next_element, { buffer = true }) - vim.keymap.set("o", "B", prev_element, { buffer = true }) + keybindings.setup_keybindings({ + keys = defaults.default_keys + }) end) it("should delete next form", function() @@ -87,8 +86,36 @@ describe("motions with operator pending", function() cursor = { 1, 4 }, }) end) - after_each(function() - vim.keymap.del("o", "E") - vim.keymap.del("o", "B") +end) + +describe("motions with operator pending and v:count", function() + before_each(function() + keybindings.setup_keybindings({ + keys = defaults.default_keys + }) + end) + + it("should delete the next 2 elements", function() + prepare_buffer({ + content = "(aa bb cc)", + cursor = { 1, 4 }, + }) + feedkeys("d2") + expect({ + content = "(aa )", + cursor = { 1, 4 }, + }) + end) + + it("should delete the previous 2 elements", function() + prepare_buffer({ + content = "(aa bb cc)", + cursor = { 1, 8 }, + }) + feedkeys("d2") + expect({ + content = "(aa )", + cursor = { 1, 4 }, + }) end) end) diff --git a/tests/nvim-paredit/text_object_selections_spec.lua b/tests/nvim-paredit/text_object_selections_spec.lua index 9abdf51..6e1b8b5 100644 --- a/tests/nvim-paredit/text_object_selections_spec.lua +++ b/tests/nvim-paredit/text_object_selections_spec.lua @@ -1,4 +1,5 @@ -local paredit = require("nvim-paredit.api") +local keybindings = require("nvim-paredit.utils.keybindings") +local defaults = require("nvim-paredit.defaults") local prepare_buffer = require("tests.nvim-paredit.utils").prepare_buffer local feedkeys = require("tests.nvim-paredit.utils").feedkeys @@ -9,8 +10,9 @@ describe("form deletions", function() vim.api.nvim_buf_set_option(0, "filetype", "clojure") before_each(function() - vim.keymap.set("o", "af", paredit.select_around_form, { buffer = true, remap = false }) - vim.keymap.set("o", "if", paredit.select_in_form, { buffer = true, remap = false }) + keybindings.setup_keybindings({ + keys = defaults.default_keys, + }) end) it("should delete the form", function() @@ -78,8 +80,9 @@ describe("form selections", function() vim.api.nvim_buf_set_option(0, "filetype", "clojure") before_each(function() - vim.keymap.set("v", "af", paredit.select_around_form, { buffer = true, remap = false }) - vim.keymap.set("v", "if", paredit.select_in_form, { buffer = true, remap = false }) + keybindings.setup_keybindings({ + keys = defaults.default_keys, + }) end) it("should select the form", function()