Skip to content

Commit

Permalink
Replace operator keymap field with smarter funcs
Browse files Browse the repository at this point in the history
Currently the E/B motions are configured with `operator = true` which
additionally adds the `o` mode keymap with the fn wrapped in `normal! v`
to allow it to function in operator-pending mode.

This moves responsibility into the keymap setup phase instead of the
function handling the action, and in the current implementation won't
work in visual mode operator-pending states.

This commit shifts the responsibility to the motions API by allowing
them to detect the current mode and conditionally set `normal! v`.
  • Loading branch information
julienvincent committed Aug 12, 2023
1 parent bdd11cb commit f37bcec
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 42 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
},
}
})
Expand Down
11 changes: 11 additions & 0 deletions lua/nvim-paredit/api/motions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,22 @@ 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()
ensure_visual_if_operator_pending()
M._move_to_element(vim.v.count1, true)
end

function M.move_to_next_element()
ensure_visual_if_operator_pending()
M._move_to_element(vim.v.count1, false)
end

Expand Down
8 changes: 4 additions & 4 deletions lua/nvim-paredit/defaults.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
},
}

Expand Down
6 changes: 6 additions & 0 deletions lua/nvim-paredit/utils/common.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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

23 changes: 0 additions & 23 deletions lua/nvim-paredit/utils/keybindings.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
9 changes: 4 additions & 5 deletions tests/nvim-paredit/operator_motion_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
13 changes: 8 additions & 5 deletions tests/nvim-paredit/text_object_selections_spec.lua
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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()
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit f37bcec

Please sign in to comment.