(Range) preserve visual selection orientation in from_motion
Some checks failed
ci / ci (push) Failing after 1m7s

This commit is contained in:
2026-04-29 22:36:46 -06:00
parent 8b7d7dc968
commit 4ce60fce2e
4 changed files with 54 additions and 13 deletions

4
.gitmodules vendored
View File

@@ -2,7 +2,3 @@
path = library/busted
url = https://github.com/LuaCATS/busted
branch = main
[submodule "library/luv"]
path = library/luv
url = https://github.com/LuaCATS/luv
branch = main

Submodule library/luv deleted from 3615eb12c9

View File

@@ -542,6 +542,7 @@ function Range.from_motion(motion, opts)
local is_bracket_txtobj = is_txtobj and BRACKET_MAP[motion_rest] ~= nil
-- SECTION: Capture original state for restoration
local vinf, vinf_inverted = Range.from_vtext()
local original_state = {
winview = vim.fn.winsaveview(),
unnamed_register = vim.fn.getreg '"',
@@ -551,7 +552,8 @@ function Range.from_motion(motion, opts)
opfunc = vim.go.operatorfunc,
prev_captured_range = _G.Range__from_motion_opfunc_captured_range,
prev_mode = vim.fn.mode(),
vinf = Range.from_vtext(),
vinf = vinf,
vinf_inverted = vinf_inverted,
}
--- @type u.Range|nil
_G.Range__from_motion_opfunc_captured_range = nil
@@ -603,7 +605,9 @@ function Range.from_motion(motion, opts)
vim.fn.setpos('.', original_state.cursor)
vim.fn.setpos("'[", original_state.mark_lbracket)
vim.fn.setpos("']", original_state.mark_rbracket)
if original_state.prev_mode ~= 'n' then original_state.vinf:set_visual_selection() end
if original_state.prev_mode ~= 'n' then
original_state.vinf:set_visual_selection(original_state.vinf_inverted)
end
vim.go.operatorfunc = original_state.opfunc
_G.Range__from_motion_opfunc_captured_range = original_state.prev_captured_range
@@ -663,7 +667,8 @@ end
function Range.from_vtext()
local r = Range.from_marks('v', '.')
if vim.fn.mode() == 'V' then r = r:to_linewise() end
return r
local inverted = Pos.from_pos '.' ~= r.stop
return r, inverted
end
--- Get range information from the current text range being operated on
@@ -806,18 +811,23 @@ end
function Range:save_to_extmark() return Extmark.from_range(self, NS) end
function Range:set_visual_selection()
--- @param inverted boolean?
function Range:set_visual_selection(inverted)
if self:is_empty() then return end
if vim.api.nvim_get_current_buf() ~= self.start.bufnr then
error 'Range:set_visual_selection() called on a buffer other than the current buffer'
end
local curr_mode = vim.fn.mode()
if curr_mode ~= self.mode then vim.cmd.normal { args = { self.mode }, bang = true } end
local curr_mode = vim.fn.mode():sub(1, 1)
if curr_mode ~= self.mode then vim.cmd.normal { args = { ESC .. self.mode }, bang = true } end
self.start:save_to_pos '.'
local start, stop = self.start, self.stop
if inverted then
start, stop = stop, start
end
start:save_to_pos '.'
vim.cmd.normal { args = { 'o' }, bang = true }
self.stop:save_to_pos '.'
stop:save_to_pos '.'
end
--------------------------------------------------------------------------------

View File

@@ -468,6 +468,42 @@ describe('Range', function()
end)
end)
it('from_motion preserves visual selection orientation (cursor at start)', function()
withbuf({ 'the quick brown fox' }, function()
vim.fn.setpos('.', { 0, 1, 5, 0 })
vim.cmd.normal 'v'
vim.cmd.normal { args = { 'o' }, bang = true }
vim.fn.setpos('.', { 0, 1, 3, 0 })
vim.cmd.normal { args = { 'o' }, bang = true }
vim.cmd.normal { args = { 'o' }, bang = true }
local cursor_before = vim.fn.getpos('.')[3]
Range.from_motion 'aw'
vim.cmd.normal { args = { 'o' }, bang = true }
local cursor_after = vim.fn.getpos('.')[3]
assert.are_not_same(cursor_before, cursor_after)
end)
end)
it('from_motion preserves visual selection orientation (cursor at stop)', function()
withbuf({ 'the quick brown fox' }, function()
vim.fn.setpos('.', { 0, 1, 3, 0 })
vim.cmd.normal 'vll'
local cursor_before = vim.fn.getpos('.')[3]
Range.from_motion 'aw'
vim.cmd.normal { args = { 'o' }, bang = true }
local cursor_after = vim.fn.getpos('.')[3]
assert.are_not_same(cursor_before, cursor_after)
end)
end)
describe('from_motion with max_lines', function()
it('returns nil when bracket chars are beyond max_lines', function()
-- A large block where { and } are far from cursor