Skip to content

Commit

Permalink
Merge pull request #28 from brenton-leighton/get_two_chars
Browse files Browse the repository at this point in the history
Fix incorrect count/register for custom key maps
  • Loading branch information
brenton-leighton authored Dec 30, 2023
2 parents 16f57bf + c030548 commit bdc1da0
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 36 deletions.
91 changes: 84 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ The plugin doesn't initially bind any keys, but creates three commands:
| `MultipleCursorsAddToWordUnderCursor` | Search for the word under the cursor and add cursors to each match. <br/> If called in visual mode, the visual area is saved and visual mode is exited. When the command is next called in normal mode, cursors will be added to only the matching words that begin within the saved visual area. |

These commands can be bound to keys, e.g.:
```
```lua
vim.keymap.set({"n", "i"}, "<C-Down>", "<Cmd>MultipleCursorsAddDown<CR>")
```
to bind the `MultipleCursorsAddDown` command to `Ctrl+Down` in normal and insert modes.
Expand All @@ -40,9 +40,9 @@ to bind the `MultipleCursorsAddDown` command to `Ctrl+Down` in normal and insert
### Using [lazy.nvim](https://github.com/folke/lazy.nvim)

Add a section to the Lazy plugins table, e.g.:
```
```lua
"brenton-leighton/multiple-cursors.nvim",
config = true,
opts = {},
keys = {
{"<C-Down>", "<Cmd>MultipleCursorsAddDown<CR>", mode = {"n", "i"}},
{"<C-j>", "<Cmd>MultipleCursorsAddDown<CR>"},
Expand Down Expand Up @@ -111,7 +111,7 @@ Notable missing functionality:

Options can be configured by providing an options table to the setup function, e.g. with Lazy:

```
```lua
"brenton-leighton/multiple-cursors.nvim",
opts = {
enable_split_paste = false,
Expand Down Expand Up @@ -162,20 +162,97 @@ This option can be used to disabled any of the default key maps. Each element in

Default value: `{}`

This option allows for mapping keys to custom functions for use with multiple cursors. Each element in the `custom_key_maps` table must have three elements:
This option allows for mapping keys to custom functions for use with multiple cursors. Each element in the `custom_key_maps` table must have three or four elements:

- Mode (string|table): Mode short-name string (`"n"`, `"i"`, or `"v"`), or a table of mode short-name strings
- Mapping lhs (string|table): [Left-hand side](https://neovim.io/doc/user/map.html#%7Blhs%7D) of a mapping string, e.g. `">>"`, `"<Tab>"`, or `"<C-/>"`, or a table of lhs strings
- Function: Lua function, e.g. `function() vim.cmd("ExampleCommand") end`
- Function: A Lua function that will be called at each cursor, which receives [`register`](https://neovim.io/doc/user/vvars.html#v%3Aregister) and [`count1`](https://neovim.io/doc/user/vvars.html#v%3Acount1) as arguments
- Option: A optional string containing "m", "c", or "cc". These enable getting input from the user, which is then forwarded to the function:
- "m" indicates that a motion command (which can include a count in addition to the `count1` variable) is requested (i.e. operator pending mode)
- "c" indicates that a printable character is requested (e.g. for character search)
- "cc" indicates that two printable characters are requested
- If valid input isn't given by the user the function will not be called
- There will be no indication that Neovim is waiting for a motion command or character

```lua
opts = {
custom_key_maps = {

-- No option
{"n", "<Leader>a", function(register, count1)
vim.print(register .. count1 .. "a")
end}

-- Motion command
{"n", "<Leader>b", function(register, count1, motion_cmd)
vim.print(register .. count1 .. "b" .. motion_cmd)
end, "m"}

When a mapping is executed the given function will be called at each cursor.
-- Character
{"n", "<Leader>c", function(register, count1, char)
vim.print(register .. count1 .. "c" .. char)
end, "c"}

-- Two characters
{"n", "<Leader>d", function(register, count1, char1, char2)
vim.print(register .. count1 .. "d" .. char1 .. char2)
end, "cc"}

}
}
```

See the [Plugin compatibility](#plugin-compatibility) section for more examples.

### `pre_hook` and `post_hook`

Default values: `nil`

These options are to provide functions that are called a the start of initialisation and at the end of de-initialisation respectively.

E.g. to disable `cursorline` while multiple cursors are active:

```lua
opts = {
pre_hook = function()
vim.opt.cursorline = false
end,
post_hook = function()
vim.opt.cursorline = true
end,
}
```

## Plugin compatibility

### [`chrisgrieser/nvim-spider`](https://github.com/chrisgrieser/nvim-spider)

Improves `w`, `e`, and `b` motions. `count1` must be set before the motion function is called.

```lua
opts = {
custom_key_maps = {
-- w
{{"n", "x"}, "w", function(_, count1)
vim.cmd("normal! " .. count1)
require('spider').motion('w')
end},

-- e
{{"n", "x"}, "e", function(_, count1)
vim.cmd("normal! " .. count1)
require('spider').motion('e')
end},

-- b
{{"n", "x"}, "b", function(_, count1)
vim.cmd("normal! " .. count1)
require('spider').motion('b')
end},
}
}
```

## API

### `add_cursor(lnum, col, curswant)`
Expand Down
39 changes: 32 additions & 7 deletions lua/multiple-cursors/input.lua
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,43 @@ local search_motions = {
["T"] = true,
}

-- Get a standard character
-- Returns nil for anything else
-- ToDo check for escape, tab and enter?
-- Get a standard character and returns nil for anything else
function M.get_char()

local char = vim.fn.getcharstr()
if #char == 1 then
return char
else
local dec = vim.fn.getchar()

-- Check for non ASCII special characters
if type(dec) ~= "number" then
return nil
end

-- Check for ASCII special characters
if dec < 32 or dec == 127 then
return nil
end

-- Convert decimal to char and return
return vim.fn.nr2char(dec)

end

-- Get two standard characters
function M.get_two_chars()

local char1 = M.get_char()

if char1 == nil then
return nil, nil
end

local char2 = M.get_char()

if char2 == nil then
return nil, nil
end

return char1, char2

end

-- Wait for a motion command
Expand Down
90 changes: 68 additions & 22 deletions lua/multiple-cursors/key_maps.lua
Original file line number Diff line number Diff line change
Expand Up @@ -129,38 +129,51 @@ end

-- Function to execute a custom key map
local function custom_function(func)

-- Save register and count1 because they may be lost
local register = vim.v.register
local count1 = vim.v.count1

-- Call func for the real cursor
func()
func(register, count1)

-- Call func for each virtual cursor and set the virtual cursor position
virtual_cursors.edit_with_cursor(function(vc)
func()
func(register, count1)
vc:save_cursor_position()
end)
end

local function custom_function_with_motion(func)

-- Save register and count1 because they may be lost
local register = vim.v.register
local count1 = vim.v.count1

-- Get a printable character
local motion = input.get_motion_cmd()
local motion_cmd = input.get_motion_cmd()

if motion == nil then
if motion_cmd == nil then
return
end

-- Call func for the real cursor
func(motion)
func(register, count1, motion_cmd)

-- Call func for each virtual cursor and set the virtual cursor position
virtual_cursors.edit_with_cursor(function(vc)
func(motion)
func(register, count1, motion_cmd)
vc:save_cursor_position()
end)

end

local function custom_function_with_char(func)

-- Save register and count1 because they may be lost
local register = vim.v.register
local count1 = vim.v.count1

-- Get a printable character
local char = input.get_char()

Expand All @@ -169,11 +182,35 @@ local function custom_function_with_char(func)
end

-- Call func for the real cursor
func(char)
func(register, count1, char)

-- Call func for each virtual cursor and set the virtual cursor position
virtual_cursors.edit_with_cursor(function(vc)
func(char)
func(register, count1, char)
vc:save_cursor_position()
end)

end

local function custom_function_with_two_chars(func)

-- Save register and count1 because they may be lost
local register = vim.v.register
local count1 = vim.v.count1

-- Get two printable characters
local char1, char2 = input.get_two_chars()

if char1 == nil or char2 == nil then
return
end

-- Call func for the real cursor
func(register, count1, char1, char2)

-- Call func for each virtual cursor and set the virtual cursor position
virtual_cursors.edit_with_cursor(function(vc)
func(register, count1, char1, char2)
vc:save_cursor_position()
end)

Expand All @@ -182,29 +219,38 @@ end
-- Set any custom key maps
-- This is a separate function because it's also used by the LazyLoad autocmd
function M.set_custom()

for i=1, #custom_key_maps do
local custom_modes = wrap_in_table(custom_key_maps[i][1])
local custom_keys = wrap_in_table(custom_key_maps[i][2])
local func = custom_key_maps[i][3]

local opt = nil
custom_key_map = custom_key_maps[i]

local custom_modes = wrap_in_table(custom_key_map[1])
local custom_keys = wrap_in_table(custom_key_map[2])
local func = custom_key_map[3]

if #custom_key_maps[i] == 4 then
opt = custom_key_maps[i][4]
local wrapped_func = function() custom_function(func) end

-- Change wrapped_func if there's a valid option
if #custom_key_map >= 4 then
local opt = custom_key_map[4]

if opt == "m" then -- Motion character
wrapped_func = function() custom_function_with_motion(func) end
elseif opt == "c" then -- Standard character
wrapped_func = function() custom_function_with_char(func) end
elseif opt == "cc" then -- Two standard characters
wrapped_func = function() custom_function_with_two_chars(func) end
end
end

for j=1, #custom_modes do
for k=1, #custom_keys do
if opt == "m" then
vim.keymap.set(custom_modes[j], custom_keys[k], function() custom_function_with_motion(func) end)
elseif opt == "c" then
vim.keymap.set(custom_modes[j], custom_keys[k], function() custom_function_with_char(func) end)
else
vim.keymap.set(custom_modes[j], custom_keys[k], function() custom_function(func) end)
end
vim.keymap.set(custom_modes[j], custom_keys[k], wrapped_func)
end
end
end

end -- for each custom key map

end

-- Set key maps used by this plug-in
Expand Down

0 comments on commit bdc1da0

Please sign in to comment.