引言
曾经使用过一段时间的VsCode,发现选中需要移动的代码段以后,再按ALT+方向键,就可以实现代码块上下整体移动,感觉这个功能非常实用。
于是想,能不能在Vim利用脚本实现这个快捷功能呢?在网上搜索了一下,发现已经有以下办法,参考移动一行或一段代码,这个办法是vimrc配置文件中添加:
nmap <M-j> mz:m+<cr>`z
nmap <M-k> mz:m-2<cr>`z
vmap <M-j> :m'>+<cr>`<my`>mzgv`yo`z
vmap <M-k> :m'<-2<cr>`>my`<mzgv`yo`z
但经过实验,发现这个办法在移动多行到顶行或底行后,如果再多按几下Alt+J/K,就会出错,导致代码顺序混乱,那么有没有其他更完美的办法呢?研究了一下vimscript的内置函数setline()和getpos()后,采用了下面的办法可以完美实现。
总体功能描述是这样的:
- 普通模式和插入模式时,按Alt+J/K实现光标所在行上下移动;
- 在可视模式时,选中多行,然后按Alt+J/K实现整段代码上下移动。
1. 脚本代码
function! s:moveup_line()
let cur_pos = getpos('.') "获取当前光标位置
"如果已经是最上一行,则直接返回
if cur_pos[1] == 1
return
endif
let tgt_line = cur_pos[1] - 1 "获得上一行的行号
let tmp = getline(tgt_line) "将上一行的内容临时存储
call setline(tgt_line,getline(cur_pos[1])) "将上一行的内容替换成当前行
call setline(cur_pos[1],tmp) "将当前行的内容替换成上一行
let cur_pos[1] -= 1 "将当前光标的行号减1,得到目标位置"
call setpos('.',cur_pos) "修改当前光标位置
endfunction
function! s:movedown_line()
let cur_pos = getpos('.') "获取当前光标位置
"如果已经是最底一行,则直接返回
if cur_pos[1] == line('$')
return
endif
let tgt_line = cur_pos[1] + 1 "获得下一行的行号
let tmp = getline(tgt_line) "将下一行的内容临时存储
call setline(tgt_line,getline(cur_pos[1])) "将下一行的内容替换成当前行
call setline(cur_pos[1],tmp) "将当前行的内容替换成下一行
let cur_pos[1] += 1 "将当前光标的行号+1,得到目标位置"
call setpos('.',cur_pos) "修改当前光标位置
endfunction
function! s:moveup_multlines() range
"获取选择范围的端点的位置信息
let start_mark = getpos("'<")
let end_mark = getpos("'>")
"对代码块的位置进行判断,当已经为最顶层时,直接退出函数
if start_mark[1] == 1
return
endif
"利用getling()和setline()内置函数实现代码块整体移动
let save_curpos = getpos('.')
let buffer_lines = getline(start_mark[1],end_mark[1])
call add(buffer_lines, getline(start_mark[1] - 1))
call setline(start_mark[1]-1,buffer_lines)
"调整选区范围和当前光标的位置
let start_mark[1] -= 1
let end_mark[1] -= 1
let save_curpos[1] -= 1
call setpos("'<",start_mark)
call setpos("'>",end_mark)
call setpos('.',save_curpos)
endfunction
function! s:movedown_multlines() range
"获取选择范围的端点的位置信息
let start_mark = getpos("'<")
let end_mark = getpos("'>")
"对代码块的位置进行判断,当已经为最底层时,直接退出函数
if end_mark[1] == line('$')
return
endif
"利用getling()和setline()内置函数实现代码块整体移动
let save_curpos = getpos('.')
let buffer_lines = [getline(end_mark[1] + 1)]
call extend(buffer_lines, getline(start_mark[1],end_mark[1]) )
call setline(start_mark[1],buffer_lines)
"调整选区范围和当前光标的位置
let start_mark[1] += 1
let end_mark[1] += 1
let save_curpos[1] += 1
call setpos("'<",start_mark)
call setpos("'>",end_mark)
call setpos('.',save_curpos)
endfunction
2. 键盘映射配置
noremap <silent> <M-k> :call <SID>moveup_line()<CR>
noremap <silent> <M-j> :call <SID>movedown_line()<CR>
inoremap <silent> <M-k> <ESC>:call <SID>moveup_line()<CR>a
inoremap <silent> <M-j> <ESC>:call <SID>movedown_line()<CR>a
vnoremap <silent> <M-k> :call <SID>moveup_multlines()<CR>gv "后面必须添加‘gv’才可重新进入可视模式
vnoremap <silent> <M-j> :call <SID>movedown_multlines()<CR>gv
3. 使用脚本
啰嗦几句,将上述的脚本代码和键盘配置写到vimrc文件中。
Windows中:vimrc文件在 “C:\users\改成自己的用户名\_vimrc“或“Vim安装目录\_vimrc"
Linux中:~/.vimrc
重启Vim,就可以愉快的使用ALT+J/K实现代码的上下移动了。
如果Alt键映射有问题的,请参考知乎Vim大佬韦易笑的文章
Vim 中正确使用 Alt映射