diff --git a/README.md b/README.md index 5fc4137..91a20e2 100644 --- a/README.md +++ b/README.md @@ -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 @@ -51,6 +52,7 @@ keys = { {"A", "MultipleCursorsAddMatchesV", mode = {"n", "x"}}, {"d", "MultipleCursorsAddJumpNextMatch", mode = {"n", "x"}}, {"D", "MultipleCursorsJumpNextMatch"}, + {"l", "MultipleCursorsLockToggle", mode = {"n", "x"}}, }, ``` @@ -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 @@ -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 diff --git a/lua/multiple-cursors/extmarks.lua b/lua/multiple-cursors/extmarks.lua index 4b6f9dd..e81d67e 100644 --- a/lua/multiple-cursors/extmarks.lua +++ b/lua/multiple-cursors/extmarks.lua @@ -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 diff --git a/lua/multiple-cursors/init.lua b/lua/multiple-cursors/init.lua index f5ecdcf..81298ec 100644 --- a/lua/multiple-cursors/init.lua +++ b/lua/multiple-cursors/init.lua @@ -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 @@ -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 diff --git a/lua/multiple-cursors/move_special.lua b/lua/multiple-cursors/move_special.lua index c1ab5bc..169f5d7 100644 --- a/lua/multiple-cursors/move_special.lua +++ b/lua/multiple-cursors/move_special.lua @@ -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 diff --git a/lua/multiple-cursors/normal_mode_change.lua b/lua/multiple-cursors/normal_mode_change.lua index 8fc2d59..b3e978d 100644 --- a/lua/multiple-cursors/normal_mode_change.lua +++ b/lua/multiple-cursors/normal_mode_change.lua @@ -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 @@ -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 @@ -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 diff --git a/lua/multiple-cursors/virtual_cursor.lua b/lua/multiple-cursors/virtual_cursor.lua index 45ac92a..4f65042 100644 --- a/lua/multiple-cursors/virtual_cursor.lua +++ b/lua/multiple-cursors/virtual_cursor.lua @@ -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 diff --git a/lua/multiple-cursors/virtual_cursors.lua b/lua/multiple-cursors/virtual_cursors.lua index 845904f..27b7e59 100644 --- a/lua/multiple-cursors/virtual_cursors.lua +++ b/lua/multiple-cursors/virtual_cursors.lua @@ -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 @@ -107,6 +110,7 @@ end -- Clear all virtual cursors function M.clear() virtual_cursors = {} + locked = false end function M.update_extmarks() @@ -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() @@ -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 @@ -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) @@ -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 @@ -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() @@ -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 diff --git a/lua/multiple-cursors/visual_mode.lua b/lua/multiple-cursors/visual_mode.lua index 63de6b2..ab32a9f 100644 --- a/lua/multiple-cursors/visual_mode.lua +++ b/lua/multiple-cursors/visual_mode.lua @@ -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})