0

So I am trying to create a Lua function to move the visual selection elsewhere. For question simplicity, lets assume 1 line above the current visual selection position.

This question is not about moving the text itself, that's trivial. It is about moving the selection cursors back to the now moved text.

First, something like vim.api.nvim_input('koko') doesn't work because doesn't support arbitrary positions. It also doesn't work because it seems there is a race condition and the move hasn't occurred yet and it just destroys the selection.

This:

local pos1 = vim.fn.getpos('v')                                                                     
local pos2 = vim.fn.getpos('.')

pos1[2] = pos1[2] - 1 
pos2[2] = pos2[2] - 1

vim.fn.setpos('.', pos2)
vim.fn.setpos('v', pos1)

doesn't work because it says "illegal argument" in the error prompt. Apparently, moving v isn't supported. Using '</'> doesn't work because it's not the same thing.

This also doesn't work and I have no idea why:

vim.api.nvim_win_set_cursor(0, pos2)
vim.api.nvim_input("o")
vim.api.nvim_win_set_cursor(0, pos1)

So, how can I set the visual selection using the API in a way that's not horrible and also reliable?

8
  • Welcome to Vi and Vim! What is the context in which you're calling this code? In many contexts, you'll be out of Visual mode already (for example, if you use : you go to Command-line mode.) If you're creating a mapping, then something simple such as xnoremap <Up> koko seems to work fine... Is that what you wanted?
    – filbranden
    Jan 1, 2022 at 20:27
  • I call vim.api.nvim_command(row_begin..",".. row_end .."move "..(row_begin-2)) before trying to restore the selection. I called getpos before executing the command above and confirmed the row/col are both correct using print(). I also tried to call <esc><esc>v and it didn't work. Is this because calling nvim_win_set_cursor or setpos leaves the visual mode and magically return to it?
    – Elv13
    Jan 1, 2022 at 22:00
  • A :move will preserve marks, including those of the selection itself... How about xnoremap <silent> <Up> :m -2<CR>gv ?
    – filbranden
    Jan 1, 2022 at 22:38
  • Maybe I missed something, but this doesn't seem to neither move the text nor correctly move the cursor. If I mix it with the line in my previous comment, it moves the text, but the selection gets destroyed. Also, isn't there some sort of better APIs (I could not find any). I am trying to avoid using nvim_input because it doesn't have a noremap mode. It isn't reliable when called from a (lua) script function. I understand the v mode isn't the most beloved feature by Vim users, but I find it strange it is so damn near impossible to move a line up while preserving the selection...
    – Elv13
    Jan 2, 2022 at 3:25
  • 1
    That mapping works for me to move the text and keep the selection (so you can keep tapping the key again to keep moving the block around.) See for example vim.fandom.com/wiki/… which recommends that tip, actually together with a gv=gv command, with the = reinfecting the block when necessary... Note that this only works in Visual mode (it's a xnoremap)!
    – filbranden
    Jan 2, 2022 at 3:44

1 Answer 1

0

I had the same problem and your question helped me to solve it for me. I've created following functions in dragging.lua:

local dragging = {}

dragging.one_line_up = function ()
  vim.cmd(":m -2")
end

dragging.one_line_down = function ()
  vim.cmd(":m +1")
end

dragging.mult_lines_down = function ()
  local crusor_pos_start = vim.fn.getpos("v") -- returns x y from start [1]=col [2]=row
  local crusor_pos_end = vim.fn.getpos(".")
  -- vim.cmd('echo "start: ' .. crusor_pos_start[1] .. ' ' .. crusor_pos_start[2] .. ', end: ' .. crusor_pos_end[1] .. ' ' .. crusor_pos_end[2] .. '"')

  -- moving using move command  4,4 move 5 puts the line 4 at pos 5
  --                            9,12 move 13 puts block 9-12 one line down
  local move_cmd = crusor_pos_start[2] .. ',' .. crusor_pos_end[2] .. ' m ' .. crusor_pos_end[2] + 1  -- .. is string concat
  -- vim.cmd('echo "' .. move_cmd .. '"')
  vim.cmd(':' .. move_cmd)

  -- select the moved block
  vim.cmd([[normal! :noh]])                                           -- enter normal mode
  vim.api.nvim_win_set_cursor(0, {crusor_pos_start[2]+1, 0})          -- put crusor in line start +1
  vim.cmd([[normal V]])                                               -- enter V mode
  vim.api.nvim_win_set_cursor(0, {crusor_pos_end[2]+1, 0})            -- put cror in line stop +1
end

dragging.mult_lines_up = function ()
  local crusor_pos_start = vim.fn.getpos("v") -- returns x y from start [1]=col [2]=row
  local crusor_pos_end = vim.fn.getpos(".")

  -- moving using move command  4,4 move 5 puts the line 4 at pos 5
  --                            9,12 move 13 puts block 9-12 one line down
  local move_cmd = crusor_pos_start[2] .. ',' .. crusor_pos_end[2] .. ' m ' .. crusor_pos_start[2] - 2
  -- vim.cmd('echo "' .. move_cmd .. '"')
  vim.cmd(':' .. move_cmd)

  -- select the moved block
  vim.cmd([[normal! :noh]])                                           -- enter normal mode
  vim.api.nvim_win_set_cursor(0, {crusor_pos_start[2]-1, 0})          -- put crusor in line start -1
  vim.cmd([[normal V]])                                               -- enter V mode
  vim.api.nvim_win_set_cursor(0, {crusor_pos_end[2]-1, 0})            -- put cror in line stop -1
end
return dragging

Then I've set these keybindings in init.lua:

local dragging_lines = require "custom.dragging_lines"    
vim.keymap.set({'n', 'i'}, '<A-Up>', dragging_lines.one_line_up)
vim.keymap.set({'n', 'i'}, '<A-Down>', dragging_lines.one_line_down)
vim.keymap.set({'n', 'i'}, '<A-k>', dragging_lines.one_line_up)
vim.keymap.set({'n', 'i'}, '<A-j>', dragging_lines.one_line_down)

vim.keymap.set('v', '<A-j>', dragging_lines.mult_lines_down)
vim.keymap.set('v', '<A-Down>', dragging_lines.mult_lines_down)
vim.keymap.set('v', '<A-k>', dragging_lines.mult_lines_up)
vim.keymap.set('v', '<A-Up>', dragging_lines.mult_lines_up)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.