Closed swarn closed 22 hours ago
Hi swarn
there are no guidelines for issues/contributing because i find them annoying.
i've added Cursor:setVisualLines(string[])
which does the job:
local thisLines = thisCursor:getVisualLines()
local otherLines = otherCursor:getVisualLines()
thisCursor:setVisualLines(otherLines)
otherCursor:setVisualLines(thisLines)
works very well for visual char and line mode. visual block mode needs some work but looks very complicated.
try :Lazy update
and let me know if any issues.
Nice!
The API works well. This swap
function is now very clean. Another example: I was happy to write a "run arbitrary Ex command at cursors" function and find that it is as simple as:
local function runCmdAtCursors(command)
mc.action(function(ctx)
command = command or vim.fn.input "Command: "
if not command or command == "" then return end
ctx:forEachCursor(function(cursor)
cursor:perform(function() vim.cmd(command) end)
end)
end)
end
I should have tested more before commenting!
This works for selections on different lines, but not selections on the same line — which I know, I didn't mention earlier. Starting here:
Sometimes the result is this:
And somtimes it just throws an error:
...multicursor.nvim/lua/multicursor-nvim/cursor-manager.lua:1208: attempt to index field '_redoChangePos' (a nil value)
The code for the above is what you expect, but for the sake of completeness:
I've come up with a contrived example that I think covers everything. Starting with the following text:
You can select all, then match (
to create cursors, then vi)
(from mini-ai) to select inside parentheses including whitespace. By the way, I like how this plugin instantly works with custom textobjects.
The swap
function using the new setVisualLines
method results in:
The selection of the 222
region has vanished.
Instead we'd like to see:
After two swaps back:
And this would be the result after three swaps:
The reason I'm hoping for this super-generalized behavior is that multicursor can generalize and replace a lot of "splitjoin" and swap functions.
@swarn I have pushed out a new fix. try :Lazy update
Seems to work well for cursors on the same line. I also made undo/redo work properly. Let me know if any issues.
Glad you like the api. Since I wrote all the examples/default behaviours with the same api it is quite capable.
@jake-stewart, this looks good. It works for swapping the words in a line:
aaa bbb ccc
As well as paragraphs:
aaa aaa
aaaa
bbb
ccc
ccc
ccc
It still struggles with the contrived, mixed example above. Starting with
Selecting inside parens
One swap backwards isn't quite right: it lost the trailing newline for the 4 block. Also, the 4 block selection now includes the leading paren.
Which we can see if we swap backwards again
Also, undo doesn't work properly after that second swap. It looks like this:
@swarn should be good for you now
💯
It works for all the tests I can come up with.
I see you're feeding the lines into a register and pasting it, then restoring the register. Is the feedkeys
approach better than trying to use nvim_buf_set_text
? I tried to use the latter without success.
by using feedkeys
, i let vim handle how replacing a visual selection works. i
pass l
, b
or c
to vim.fn.setreg
to define that the lines are
line/block/char. contrast to nvim_buf_set_text
where i would need to call it
per each line of a visual block selection.
replacing a visual selection with P
automatically sets the change marks which
i use to set the new cursor position and visual selection.
it would be very tricky with nvim_buf_set_text
since i would need to manually
calculate the new visual selection. for visual char mode, the visual end column
is the length of the last line, unless the visual selection spans one line then
it is the visual start column + length of the last line. similar edge cases
exist for the other visual modes.
also nvim_buf_set_text
does not allow including the eol character in a range
since it is considered "out of range". i would need to handle this case by
decreasing the end line by one and setting end column to zero.
Ah, it makes to use P
and let vim handle the bookkeeping for you. Thanks for explaining!
@swarn i have added swapCursors
to the examples.
sidenote, i added seekCursor
and seekBoundaryCursor
, which simplifies the code a little bit more.
--- @param direction -1 | 1
--- @param wrap? boolean
function examples.swapCursors(direction, wrap)
mc.action(function(ctx)
local mainCursor = ctx:mainCursor()
local otherCursor = ctx:seekCursor(
mainCursor:getPos(), direction, wrap)
if otherCursor and otherCursor ~= mainCursor then
local mainLines = mainCursor:getVisualLines()
local otherLines = otherCursor:getVisualLines()
mainCursor:setVisualLines(otherLines)
otherCursor:setVisualLines(mainLines)
otherCursor:select()
end
end)
end
Hi Jake, nice plugin!
This isn't so much an "issue" as a question/feedback; please ignore/delete as desired. Maybe a wiki or discussions for the repo?
I'd like to implement
swapCursors
. Similar notion to the transpose function, but simply exchanges the selected region under the main cursor with an adjacent region. Also, I'd like it to work with multiple line selections. So given the following textit would be easy to split into paragraphs, then reorder the paragraphs.
The
transpose
example is good, but only works with single lines. It seems like callingnvim_buf_set_text
in anmc.action
doesn't get repeated. So I thought I'd use a few registers. What I have isThis works the first time I call it, but has errors on multi-line selections after the first time. I think this is something to do with restoring the visual selection at the cursor after the paste operation.
How about a
cursor:setLines()
method :).