Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes #42: Exclusive motion support #46

Merged
merged 6 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 32 additions & 7 deletions lua/nvim-paredit/api/motions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ local function get_next_node_from_cursor(lang, reversed)
end
end

function M._move_to_element(count, reversed, is_head)
function M._move_to_element(count, reversed, is_head, is_exclusive)
is_exclusive = is_exclusive or false
is_head = is_head or false
local lang = langs.get_language_api()

Expand Down Expand Up @@ -97,39 +98,61 @@ function M._move_to_element(count, reversed, is_head)
if is_in_middle then
count = count - 1
end
local sibling = traversal_fn(current_node, {
local sibling, count_left = traversal_fn(current_node, {
lang = lang,
count = count,
})

if sibling then
if is_head then
if is_head and count_left > 1 and not reversed then
is_head = false
is_exclusive = false
next_pos = { sibling:end_() }
elseif is_head then
next_pos = { sibling:start() }
else
next_pos = { sibling:end_() }
end
elseif is_head and not reversed then
-- convert head to tail motion
is_head = false
is_exclusive = false
next_pos = { current_node:end_() }
end
end

if not next_pos then
return
end

local offset = 0
if is_exclusive then
if reversed then
offset = 1
else
offset = -1
end
end

if is_head then
cursor_pos = { next_pos[1] + 1, next_pos[2] }
cursor_pos = { next_pos[1] + 1, next_pos[2] + offset }
else
cursor_pos = { next_pos[1] + 1, next_pos[2] - 1 }
cursor_pos = { next_pos[1] + 1, next_pos[2] - 1 + offset }
end

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.
-- returns true if operator mode is on, false otherwise
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()
return true
end
return false
end

local function move_to_form_edge(form_node, direction, lang)
Expand Down Expand Up @@ -196,10 +219,12 @@ function M.move_to_next_element_tail()
M._move_to_element(count, false, false)
end

-- also jumps to current element tail if there is no
-- 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, true)
local is_operator = ensure_visual_if_operator_pending()
M._move_to_element(count, false, true, is_operator)
end

function M.move_to_parent_form_start()
Expand Down
4 changes: 2 additions & 2 deletions lua/nvim-paredit/utils/traversal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ end
local function get_sibling_ignoring_comments(node, opts)
local sibling = opts.sibling_fn(node)
if not sibling then
return opts.sibling or nil
return opts.sibling or nil, opts.count + 1
end

if sibling:extra() or opts.lang.node_is_comment(sibling) then
Expand All @@ -94,7 +94,7 @@ local function get_sibling_ignoring_comments(node, opts)
return get_sibling_ignoring_comments(sibling, new_opts)
end

return sibling
return sibling, opts.count
end

function M.get_next_sibling_ignoring_comments(node, opts)
Expand Down
9 changes: 8 additions & 1 deletion tests/nvim-paredit/motion_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,17 @@ describe("motions", function()
expect({
cursor = { 1, 15 },
})
end)

it("should jump to current element's tail if there is no next element", function()
prepare_buffer({
content = "(aa (bb) @(cc) #{1})",
cursor = { 1, 15 },
})

paredit.move_to_next_element_head()
expect({
cursor = { 1, 15 },
cursor = { 1, 18 },
})
end)

Expand Down
40 changes: 38 additions & 2 deletions tests/nvim-paredit/operator_motion_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ local defaults = require("nvim-paredit.defaults")
describe("motions with operator pending", function()
before_each(function()
keybindings.setup_keybindings({
keys = defaults.default_keys
keys = defaults.default_keys,
})
end)

Expand All @@ -29,6 +29,42 @@ describe("motions with operator pending", function()
})
end)

it("should delete til next", function()
prepare_buffer({
content = "(a a) (b b)",
cursor = { 1, 0 },
})
feedkeys("d<S-w>")
expect({
content = "(b b)",
cursor = { 1, 0 },
})
end)

it("should delete form if there is no next", function()
prepare_buffer({
content = "(b b)",
cursor = { 1, 0 },
})
feedkeys("d<S-w>")
expect({
content = "",
cursor = { 1, 0 },
})
end)

it("should delete 2 forms within parent form and join up", function()
prepare_buffer({
content = {"[(a a) (b b) (c c)]"},
cursor = { 1, 1 },
})
feedkeys("2d<S-w>")
expect({
content = "[(c c)]",
cursor = { 1, 1 },
})
end)

it("should delete next form (multiline)", function()
prepare_buffer({
content = { "(a a)", ";; comment", "(b b)" },
Expand Down Expand Up @@ -91,7 +127,7 @@ end)
describe("motions with operator pending and v:count", function()
before_each(function()
keybindings.setup_keybindings({
keys = defaults.default_keys
keys = defaults.default_keys,
})
end)

Expand Down
Loading