Skip to content
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
43 changes: 37 additions & 6 deletions lua/cursor_text_objects.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@
local M = {}

local _Direction = { down = "down", up = "up" }

local unpack = unpack or table.unpack -- Future Lua versions use table.unpack
local _DirectionMark = { down = "]", up = "[" }
local _PositionType = { character_wise = "`", line_wise = "'" }

local _CURSOR
local _DIRECTION
local _OPERATOR
local _OPERATOR_FUNCTION

local unpack = unpack or table.unpack -- Future Lua versions use table.unpack

--- Check if the operatorfunc that is running will run on a whole-line.
---
--- See Also:
Expand Down Expand Up @@ -62,7 +64,7 @@ local function _adjust_marks(mode)
local inclusive_toggle = ""

if _DIRECTION == _Direction.up then
direction = "["
direction = _DirectionMark.up

if not is_line then
local buffer, row, column, offset = unpack(vim.fn.getpos("'" .. direction))
Expand All @@ -75,7 +77,7 @@ local function _adjust_marks(mode)
vim.fn.setpos("'" .. direction, { buffer, row, column, offset })
end
else
direction = "]"
direction = _DirectionMark.down

local buffer, row, column, offset = unpack(vim.fn.getpos("'" .. direction))

Expand Down Expand Up @@ -115,9 +117,38 @@ end
--- The caller context. See `:help :map-operator` for details.
---
function M.visual(mode)
local _, mark, direction = _adjust_marks(mode)
vim.fn.setpos(".", _CURSOR)

local mark

if _is_line_mode(mode) then
mark = "'" -- Includes only line information
else
mark = "`" -- Includes column information
end

local direction

if _DIRECTION == _Direction.up then
direction = _DirectionMark.up
else
direction = _DirectionMark.down
end

local _, row, column, _ = unpack(vim.fn.getpos("'" .. direction))

if mark == _PositionType.line_wise then
if direction == _DirectionMark.down then
column = #vim.fn.getline(row)
else
column = 0
end
else
column = column - 1
end

vim.cmd(string.format("normal v%s%s", mark, direction))
vim.cmd("normal v")
vim.api.nvim_win_set_cursor(0, { row, column })
end

--- Remember anything that we will need to recall once we execute `operatorfunc`.
Expand Down
169 changes: 159 additions & 10 deletions spec/cursor_text_objects_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,17 @@ local function _call_command(keys)
end

--- Add mappings for unittests.
local function _initialize_mappings()
vim.keymap.set("o", "[", "<Plug>(cursor-text-objects-up)")
vim.keymap.set("o", "]", "<Plug>(cursor-text-objects-down)")
vim.keymap.set("x", "[", "<Plug>(cursor-text-objects-up)")
vim.keymap.set("x", "]", "<Plug>(cursor-text-objects-down)")
---
---@param up string? The key(s) to set as the "go the top to the cursor".
---@param down string? The key(s) to set as the "go from the cursor down".
---
local function _initialize_mappings(up, down)
up = up or "["
down = down or "]"
vim.keymap.set("o", up, "<Plug>(cursor-text-objects-up)")
vim.keymap.set("o", down, "<Plug>(cursor-text-objects-down)")
vim.keymap.set("x", up, "<Plug>(cursor-text-objects-up)")
vim.keymap.set("x", down, "<Plug>(cursor-text-objects-down)")
end

--- Create a new Vim buffer with `text` contents.
Expand All @@ -49,11 +55,18 @@ local function _make_buffer(text, file_type)
end

--- Remove any the default mappings that were added from `_initialize_mappings`.
local function _revert_mappings()
vim.keymap.del("o", "[")
vim.keymap.del("o", "]")
vim.keymap.del("x", "[")
vim.keymap.del("x", "]")
---
---@param up string? The key(s) to set as the "go the top to the cursor".
---@param down string? The key(s) to set as the "go from the cursor down".
---
local function _revert_mappings(up, down)
up = up or "["
down = down or "]"

vim.keymap.del("o", up)
vim.keymap.del("o", down)
vim.keymap.del("x", up)
vim.keymap.del("x", down)
end

--- Make sure `input` becomes `expected` when `keys` are called.
Expand Down Expand Up @@ -130,6 +143,37 @@ describe("basic", function()
end)
end)

describe("custom mappings", function()
before_each(function()
_initialize_mappings("{", "}")
end)
after_each(function()
_revert_mappings("{", "}")
end)

it("works with keys, not just [ / ]", function()
_run_simple_test(
{ 2, 0 },
"c}ap",
[[
some text
more text <-- NOTE: The cursor will be set here
even more lines!
still part of the paragraph

another paragraph
with text in it
]],
[[
some text

another paragraph
with text in it
]]
)
end)
end)

describe(":help c", function()
before_each(_initialize_mappings)
after_each(_revert_mappings)
Expand Down Expand Up @@ -1235,6 +1279,111 @@ describe(":help g~", function()
end)
end)

describe(":help v", function()
before_each(_initialize_mappings)
after_each(_revert_mappings)

describe("character-wise", function()
describe("down", function()
it("works with visual selection", function()
local _, window = _make_buffer([[
A paragraph of text and sentences. Here's another sentence
that spans multiple lines <-- NOTE: The cursor will be set here
but our code should handle it. And lastly.
A sentence that starts on its own line.
]])
vim.api.nvim_win_set_cursor(window, { 2, 23 })

-- NOTE: We yank the visual selection so we can assert it later
_call_command("v]asy")

assert.same(
[[t spans multiple lines <-- NOTE: The cursor will be set here
but our code should handle it. ]],
vim.fn.getreg("")
)
end)
end)

describe("up", function()
it("works with visual selection", function()
local _, window = _make_buffer([[
A paragraph of text and sentences. Here's another sentence
that spans multiple lines <-- NOTE: The cursor will be set here
but our code should handle it. And lastly.
A sentence that starts on its own line.
]])
vim.api.nvim_win_set_cursor(window, { 2, 23 })

-- NOTE: We yank the visual selection so we can assert it later
_call_command("v[asy")

assert.same(
[[Here's another sentence
that]],
vim.fn.getreg("")
)
end)
end)
end)

describe("line-wise", function()
describe("down", function()
it("works with visual selection", function()
local _, window = _make_buffer([[
aaaa
bbbb <-- NOTE: The cursor will be set here
cccc

next
lines
blah

]])
vim.api.nvim_win_set_cursor(window, { 2, 0 })

-- NOTE: We yank the visual selection so we can assert it later
_call_command("v]apy")

assert.same(
[[
bbbb <-- NOTE: The cursor will be set here
cccc

]],
vim.fn.getreg("")
)
end)
end)

describe("up", function()
it("works with visual selection", function()
local _, window = _make_buffer([[
aaaa
bbbb
cccc

next
lines <-- NOTE: The cursor will be set here
blah

]])
vim.api.nvim_win_set_cursor(window, { 6, 4 })

-- NOTE: We yank the visual selection so we can assert it later
_call_command("v[apy")

assert.same(
[[
next
]],
vim.fn.getreg("")
)
end)
end)
end)
end)

describe(":help y", function()
before_each(_initialize_mappings)
after_each(_revert_mappings)
Expand Down
Loading