Skip to content

Commit

Permalink
Merge pull request #43 from brenton-leighton/feat_lock_cursors
Browse files Browse the repository at this point in the history
Lock virtual cursors
  • Loading branch information
brenton-leighton authored Mar 18, 2024
2 parents 9cd1b92 + 28afba2 commit be71e31
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 40 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ The plugin doesn't initially bind any keys, but creates three commands:
| `MultipleCursorsAddMatchesV` | As above, but limit matches to the previous visual area |
| `MultipleCursorsAddJumpNextMatch` | Add a virtual cursor to the word under the cursor (in normal mode) or the visual area (in visual mode), then move the real cursor to the next match |
| `MultipleCursorsJumpNextMatch` | Move the real cursor to the next match of the word under the cursor, or if `MultipleCursorsAddJumpNextMatch` was previously called in visual mode, the previously used visual area |
| `MultipleCursorsLock` | Toggle locking or unlocking the virtual cursors to allow for moving (or editing at) only the real cursor |

These commands can be bound to keys, e.g.:
```lua
Expand All @@ -51,6 +52,7 @@ keys = {
{"<Leader>A", "<Cmd>MultipleCursorsAddMatchesV<CR>", mode = {"n", "x"}},
{"<Leader>d", "<Cmd>MultipleCursorsAddJumpNextMatch<CR>", mode = {"n", "x"}},
{"<Leader>D", "<Cmd>MultipleCursorsJumpNextMatch<CR>"},
{"<Leader>l", "<Cmd>MultipleCursorsLockToggle<CR>", mode = {"n", "x"}},
},
```

Expand All @@ -65,6 +67,7 @@ This configures the plugin with the default options, and sets the following key
- `Leader+A` in normal and visual modes: `MultipleCursorsAddMatchesV`
- `Leader+d` in normal and visual modes: `MultipleCursorsAddJumpNextMatch`
- `Leader+D` in normal mode: `MultipleCursorsJumpNextMatch`
- `Leader+l` in normal and visual modes: `MultipleCursorsLock`

## Usage

Expand Down Expand Up @@ -356,4 +359,5 @@ where `lnum` is the line number of the new cursor, `col` is the column, and `cur
- In insert or replace mode, anything to do with tabs may not behave correctly, in particular if you are using less common options
- Cursors may not be positioned correctly when moving up or down over extended characters
- When using the mouse to add a cursor to an extended character, the cursor may be added to the next character
- When virtual cursors are locked, switching to or from visual mode won't update the virtual cursors and should be avoided
- Please use the [Issues](https://github.com/brenton-leighton/multiple-cursors.nvim/issues) page to report issues, and please include any relevant Neovim options
8 changes: 2 additions & 6 deletions lua/multiple-cursors/extmarks.lua
Original file line number Diff line number Diff line change
Expand Up @@ -311,12 +311,8 @@ end

-- Update all extmarks for a virtual cursor
function M.update_virtual_cursor_extmarks(vc)
if vc.within_buffer then
update_virtual_cursor_extmark(vc)
update_virtual_cursor_visual_extmarks(vc)
else -- Cursor not within buffer
M.delete_virtual_cursor_extmarks(vc)
end
update_virtual_cursor_extmark(vc)
update_virtual_cursor_visual_extmarks(vc)
end

-- Set virtual cursor position from its extmark
Expand Down
8 changes: 8 additions & 0 deletions lua/multiple-cursors/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,13 @@ function M.add_cursor(lnum, col, curswant)

end

-- Toggle locking the virtual cursors if initialised
function M.lock()
if initialised then
virtual_cursors.toggle_lock()
end
end

function M.setup(opts)

-- Options
Expand Down Expand Up @@ -576,6 +583,7 @@ function M.setup(opts)
vim.api.nvim_create_user_command("MultipleCursorsAddJumpNextMatch", M.add_cursor_and_jump_to_next_match, {})
vim.api.nvim_create_user_command("MultipleCursorsJumpNextMatch", M.jump_to_next_match, {})

vim.api.nvim_create_user_command("MultipleCursorsLock", M.lock, {})
end

return M
2 changes: 1 addition & 1 deletion lua/multiple-cursors/move_special.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ end
-- Normal mode backspace command for all virtual cursors
local function all_virtual_cursors_normal_backspace(count)
count = vim.fn.max({count, 1})
virtual_cursors.visit_in_buffer(function(vc) virtual_cursor_normal_backspace(vc, count) end)
virtual_cursors.visit_all(function(vc) virtual_cursor_normal_backspace(vc, count) end)
end

-- Normal mode backspace command
Expand Down
6 changes: 3 additions & 3 deletions lua/multiple-cursors/normal_mode_change.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ end

local function _i()
-- curswant is lost
virtual_cursors.visit_in_buffer(function(vc)
virtual_cursors.visit_all(function(vc)
vc.curswant = vc.col
end)
end
Expand All @@ -45,7 +45,7 @@ end
local function _O()

-- New line before current line
virtual_cursors.visit_in_buffer(function(vc)
virtual_cursors.visit_all(function(vc)
if vc.lnum == 1 then -- First line, move to start of line
vc.col = 1
vc.curswant = 1
Expand All @@ -71,7 +71,7 @@ end

local function _v()

virtual_cursors.visit_in_buffer(function(vc)
virtual_cursors.visit_all(function(vc)

-- Save cursor position as visual area start
vc.visual_start_lnum = vc.lnum
Expand Down
1 change: 0 additions & 1 deletion lua/multiple-cursors/virtual_cursor.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ function VirtualCursor.new(lnum, col, curswant, first)
self.visual_multiline_mark_id = 0 -- ID of the visual area extmark then spans multiple lines
self.visual_empty_line_mark_ids = {} -- IDs of the visual area extmarks for empty lines

self.within_buffer = true -- lnum is within the buffer
self.editable = true -- To disable editing the virtual cursor when
-- in collision with the real cursor
self.delete = false -- To mark the virtual cursor for deletion
Expand Down
53 changes: 25 additions & 28 deletions lua/multiple-cursors/virtual_cursors.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ local virtual_cursors = {}
-- Set to true when the cursor is being moved to suppress M.cursor_moved()
local ignore_cursor_movement = false

-- For locking the virtual cursors
local locked = false

-- Remove any virtual cursors marked for deletion
local function clean_up()
for idx = #virtual_cursors, 1, -1 do
Expand Down Expand Up @@ -107,6 +110,7 @@ end
-- Clear all virtual cursors
function M.clear()
virtual_cursors = {}
locked = false
end

function M.update_extmarks()
Expand Down Expand Up @@ -134,26 +138,32 @@ function M.cursor_moved()
for idx = #virtual_cursors, 1, -1 do
local vc = virtual_cursors[idx]

if vc.within_buffer then
-- First update the virtual cursor position from the extmark in case there
-- was a change due to editing
extmarks.update_virtual_cursor_position(vc)
-- First update the virtual cursor position from the extmark in case there
-- was a change due to editing
extmarks.update_virtual_cursor_position(vc)

-- Mark editable to false if coincident with the real cursor
vc.editable = not (vc.lnum == pos[2] and vc.col == pos[3])
-- Mark editable to false if coincident with the real cursor
vc.editable = not (vc.lnum == pos[2] and vc.col == pos[3])

-- Update the extmark (extmark is invisible if editable == false)
extmarks.update_virtual_cursor_extmarks(vc)
end
-- Update the extmark (extmark is invisible if editable == false)
extmarks.update_virtual_cursor_extmarks(vc)
end
end

function M.toggle_lock()
locked = not locked
end


-- Visitors --------------------------------------------------------------------

-- Visit all virtual cursors
function M.visit_all(func)

if locked then
return
end

-- Save cursor position
-- This is because changing virtualedit causes curswant to be reset
local cursor_pos = vim.fn.getcurpos()
Expand All @@ -168,10 +178,8 @@ function M.visit_all(func)

for idx, vc in ipairs(virtual_cursors) do

if vc.within_buffer then
-- Set virtual cursor position from extmark in case there were any changes
extmarks.update_virtual_cursor_position(vc)
end
-- Set virtual cursor position from extmark in case there were any changes
extmarks.update_virtual_cursor_position(vc)

if not vc.delete then
-- Call the function
Expand All @@ -196,23 +204,12 @@ function M.visit_all(func)

end

-- Visit virtual cursors within buffer
function M.visit_in_buffer(func)

M.visit_all(function(vc, idx)
if vc.within_buffer then
func(vc, idx)
end
end)

end

-- Visit virtual cursors within the buffer with the real cursor
function M.visit_with_cursor(func)

ignore_cursor_movement = true

M.visit_in_buffer(function(vc, idx)
M.visit_all(function(vc, idx)
vc:set_cursor_position()
func(vc, idx)
end)
Expand Down Expand Up @@ -246,7 +243,7 @@ function M.edit(func)
ignore_cursor_movement = true
extmarks.save_cursor()

M.visit_in_buffer(function(vc, idx)
M.visit_all(function(vc, idx)
if vc.editable then
func(vc, idx)
end
Expand Down Expand Up @@ -353,7 +350,7 @@ function M.visual_mode(func)
-- Save the visual area to extmarks
extmarks.save_visual_area()

M.visit_in_buffer(function(vc, idx)
M.visit_all(function(vc, idx)
-- Set visual area
vc:set_visual_area()

Expand Down Expand Up @@ -400,7 +397,7 @@ function M.can_split_paste(num_lines)
local count = 0

for _, vc in ipairs(virtual_cursors) do
if vc.within_buffer and vc.editable then
if vc.editable then
count = count + 1
end
end
Expand Down
2 changes: 1 addition & 1 deletion lua/multiple-cursors/visual_mode.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ local input = require("multiple-cursors.input")

-- Escape command
function M.escape()
virtual_cursors.visit_in_buffer(function(vc)
virtual_cursors.visit_all(function(vc)
-- Move cursor back if it's at the end of a non empty line
vc.col = vim.fn.min({vc.col, common.get_max_col(vc.lnum) - 1})
vc.col = vim.fn.max({vc.col, 1})
Expand Down

0 comments on commit be71e31

Please sign in to comment.