Mover linha inteira para cima e para baixo no Vim


311

No Notepad ++, posso usar Ctrl+ Shift+ Up/ Downpara mover a linha atual para cima e para baixo. Existe um comando semelhante a isso no Vim? Eu olhei através de guias sem fim, mas não encontrei nada.

Se não houver, como vincular a ação a essa combinação de teclas?

Edit: A resposta de Mykola funciona para todas as linhas, exceto aquelas no início e no final do buffer. Mover a primeira linha para cima ou a linha inferior para baixo exclui a linha e, ao mover a linha inferior para cima, ele pula dois espaços inicialmente, como um peão! Alguém pode oferecer refinamentos?


Eu não tinha escolha a não ser implementar a solução de script. Espero que seja limpo e adotável para suas necessidades.
Mykola Golubyev

31
Para vocês, filhos do futuro: vim.wikia.com/wiki/Moving_lines_up_or_down
guns

Não sei por que você precisa de um script, os exemplos de artigos da Wikia funcionam. Publiquei abaixo uma versão simplificada, porque o exemplo da Wikia com 3 modos de mapeamento diferentes pode ser bastante assustador (e não é realmente necessário. Se você usar apenas os mapeamentos de seleção de blocos, poderá simplesmente lembrar de bloquear a seleção (Shift V) e usar estes atalhos (veja a minha resposta abaixo). #

Screencast sobre o tema: vimcasts.org/e/26
glts

Também no vi SE
cfi

Respostas:


81

Coloque o seguinte em seu .vimrc para fazer o trabalho

noremap <c-s-up> :call feedkeys( line('.')==1 ? '' : 'ddkP' )<CR>
noremap <c-s-down> ddp

Desaparecer da linha parece um bug do Vim. Eu coloquei um truque para evitá-lo. Provavelmente existe alguma solução mais precisa.

Atualizar

Existem muitas dificuldades inexplicáveis ​​em usar apenas as combinações do Vim. Estes são falta de linha e salto de linha extra.

Então, aqui está a solução de script que pode ser colocada dentro de .vimrc ou ~ / .vim / plugin / swap_lines.vim

function! s:swap_lines(n1, n2)
    let line1 = getline(a:n1)
    let line2 = getline(a:n2)
    call setline(a:n1, line2)
    call setline(a:n2, line1)
endfunction

function! s:swap_up()
    let n = line('.')
    if n == 1
        return
    endif

    call s:swap_lines(n, n - 1)
    exec n - 1
endfunction

function! s:swap_down()
    let n = line('.')
    if n == line('$')
        return
    endif

    call s:swap_lines(n, n + 1)
    exec n + 1
endfunction

noremap <silent> <c-s-up> :call <SID>swap_up()<CR>
noremap <silent> <c-s-down> :call <SID>swap_down()<CR>

1
Tive que mudar para <cj> e <ck>, pois parece haver um conflito com minha configuração, mas resposta fantástica! Muito obrigado
user4812

1
Obrigado, ótimo para git rebase --interactive. Tinha que ligar para <cj> e <ck>. cs-up / down não funcionou.
Lari Hotari

2
Se alguém quiser um atalho diferente: c é para controle, A é para alt, eu uso <A-up>como usado no eclipse.
janeiro

1
A linha que desaparece não é um bug do Vim! É devido ao desempenho ddkPna última linha do documento. Experimente você mesmo em um arquivo de 3 linhas. Para evitar esses problemas, use os :mmapeamentos muito mais fáceis, conforme fornecidos no wiki do Vim .
Serge Stroobandt

1
Alterei as últimas linhas para <A-up>e, <A-down>mas quando tento essas teclas no vim na execução visual no tmux, a seleção desaparece e o cursor se move para cima; sem mover as linhas selecionadas.
Konrad

574

Se eu quiser trocar uma linha pela linha acima, eu normalmente faço o seguinte

ddkP

Explicação

  • O dd excluirá a linha e a adicionará ao registro padrão.
  • k subirá uma linha (j desceria uma linha)
  • P irá colar acima da linha atual

5
E está sempre disponível também. :-)
Henno 07/07

71
E ddppara mover uma linha para baixo (linha de exclusão e cole abaixo da linha atual)
bcoughlan

2
Para mover um monte de linhas, diga 4 4 dde , em seguida, vá para, diga 2 linhas e depois para :2onde deseja colar e P.
Guru

2
@Guru que deveria ser em 4ddvez de 4 dd?
Patrick McDonald

3
E dd<number>jppara mover a linha atual <número> +1 das linhas para baixo. E dd<number>kppara mover a linha atual <número> de linhas para cima.
egelev 26/09/16

230

Supondo que o cursor esteja na linha que você deseja mover.

Mover para cima e para baixo: :mpara mover

:m +1 - desce uma linha

:m -2 - subir 1 linhas

(Observe que você pode substituir +1 por qualquer número, dependendo de quantas linhas você deseja movê-lo para cima ou para baixo, ou seja, +2 o moveria para baixo 2 linhas e -3 para 2 linhas)

Para ir para uma linha específica

:set number - exibir linhas numéricas (mais fácil de ver para onde você está indo)

:m 3 - mova a linha após a terceira linha (substitua 3 por qualquer linha que desejar)

Movendo várias linhas:

V(ie Shift- V) e mova o cursor para cima e para baixo para selecionar várias linhas no VIM

uma vez selecionado, pressione :e execute os comandos acima, m +1etc


14
para mim: m -2 apenas sobe uma linha.
Fzs2

2
Eu coloquei esse método no meu vimrc, mas não consigo repetir o comando usando '.'. alguém sabe por que é isso?
Fzs2

Muito agradável! Não é tão confortável quanto a solução no Notepad ++, mas ainda é real e funcional.
Arturas M

Hermann, desculpe, erro de digitação / erro da minha parte, você está certo: m -2 subirá uma linha. Eu mudei isso. Também desculpe pela resposta muito tardia (não tenho certeza sobre a parte .vimrc) Cheers
Serg

2
Mover usando o comando funciona, se você quiser usá-lo em ligações como as do Notepad ++, conforme solicitado pelo OP, precisará digitar o seguinte em .vimrc:noremap <c-s-up> :m -2<CR> noremap <c-s-down> :m +1<CR>
kap

74

Mover uma linha para cima: ddkP

Mover uma linha para baixo: ddp


1
ter isso no vimrc não permite repetir o comando usando '.'
Fzs2

@HermannIngjaldsson, suponho que você poderia agrupar o comando em uma função se quisesse usar .para repetir.
jacobsimeon

Sim, e se você precisar mover mais linhas, poderá usar V em vez de dd.
sensorario 22/01

15

Isso funcionou para mim:

http://vim.wikia.com/wiki/Moving_lines_up_or_down_in_a_file

BTW, se você deseja usar ALT + some_key e seu terminal (urxvt faz isso) se recusa a cumprir, você deve inserir algo como isso em seu arquivo .vimrc:

" For moving lines (^] is a special character; use <M-k> and <M-j> if it works)
nnoremap ^]k mz:m-2<CR>`z==
inoremap ^]j <Esc>:m+<CR>==gi
inoremap ^]k <Esc>:m-2<CR>==gi
vnoremap ^]j :m'>+<CR>gv=`<my`>mzgv`yo`z
nnoremap ^]j mz:m+<CR>`z==
vnoremap ^]k :m'<-2<CR>gv=`>my`<mzgv`yo`z

onde ^] é um único caractere que representa a chave ALT. Para inserir esse caractere, use C+v, Escno Vim ( C+q, Escno Windows).


1
Isso é bom, usando o comando interno do Vim para mover uma linha. É mais provável que se comporte bem diante de desfazer ou erro.
Josh Lee

1
Por que você não digita Esc como <kbd>Cv<kbd/> <kbd> Esc </kbd> (linux). O Windows substitui <kbd> Cv <kbd /> por <kbd> Cq <kbd /> através da placa, mas tem a mesma maneira de introduzir qualquer chave especial no modo de inserção / command
sehe

@sehe: Obrigado, eu não sabia disso na época. Eu atualizei a resposta.
precisa saber é o seguinte

1
@RafaelRinaldi, [niv]noremapsão remaps para os modos normais | inserir | visuais. <CR>é Carriage Return( Enterchave).
vp_arth

1
@vp_arth Não, não há
sehe

13

adicione o seguinte ao arquivo ~ / .vimrc (verifique se você não tem mapeamento para n, m)

nmap n :m +1<CR>
nmap m :m -2<CR>

Agora, pressionar a ntecla moverá uma linha para baixo e mmoverá uma linha para cima.


12

No modo de comando, posicione o cursor na linha que você deseja mover para baixo e, em seguida,

ddp

Explicação: dd exclui a linha atual no buffer geral p a coloca de volta APÓS a posição do cursor ou, no caso de linhas inteiras, uma linha abaixo.

Há alguma confusão em relação aos comandos p e P em muitos documentos. Na realidade, p cola APÓS o cursor e P AT.


8

Basta adicionar este código ao .vimrc (ou .gvimrc)

nnoremap <A-j> :m+<CR>==
nnoremap <A-k> :m-2<CR>==
inoremap <A-j> <Esc>:m+<CR>==gi
inoremap <A-k> <Esc>:m-2<CR>==gi
vnoremap <A-j> :m'>+<CR>gv=gv
vnoremap <A-k> :m-2<CR>gv=gv

1
Conforme vim.wikia.com/wiki/…, consulte uma explicação abrangente.
Serge Stroobandt

Em um Mac, <A-algo> não funciona, não sei por quê. Substituí por <Cj> <Ck> e funciona bem usando a tecla Ctrl.
Cédric NICOLAS



3

Coloquei o seguinte no final do meu arquivo .vimrc:

noremap H ddkkp
noremap N ddp

Então agora 'H' e 'N' movem a linha atual para cima e para baixo, respectivamente.


H e N são realmente comandos úteis (H move o cursor para a parte superior e começando da tela, N move-se para o resultado da pesquisa anterior), por isso eu recomendo usar diferentes chaves
villapx

Entendo. Eu também os reconfigurei. Para ir para o topo da tela, pressione a e depois para cima. Sempre significa tudo nos meus livros. E para pesquisar os resultados anteriores, pressione f e depois à esquerda. F então significa encontrar. Mas para aqueles que não o têm assim, sim ... é válido.
Fzs2

1

Aqui está uma versão simplificada, para MacVim, usando os exemplos de artigos da Wikia (veja o link do comentário de gun ).

" Move selection up/down (add =gv to reindent after move)
:vmap <D-S-Up> :m-2<CR>gv
:vmap <D-S-Down> :m'>+<CR>gv

Estou usando apenas a variante de seleção de bloco, porque basta Shift-V para selecionar a linha atual e, opcionalmente, cursor para cima / baixo para selecionar mais algumas linhas.

De acordo com os atalhos acima, pressionar Cmd-Shift-Up / Down alternará a seleção de bloco para cima / baixo. "D" é a tecla Command no MacVim; para Windows, tente "C" (Controle) ou "A" (Alt) (por exemplo, <CAf> seria Control Alt f).

O artigo da Wikia adiciona "= gv" a eles, que tem o efeito de ajustar o recuo do bloco após a movimentação, com base no texto ao redor. Isso é confuso, então eu o removi e adicionei atalhos para recuar rapidamente a seleção.

" Indent selection left/right (Cmd Shift Left/Right is used for Tab switching)
:vmap <D-A-Left> <gv
:vmap <D-A-Right> >gv

Lembre-se, o mesmo pode ser feito com << e >>, mas a seleção seria perdida; portanto, esses atalhos acima permitem recuar várias vezes e ainda mover o bloco porque a seleção é mantida.

Meu MacVim está configurado para alternar as guias com Cmd-Shift-Esquerda / Direita, então usei Cmd-Alt-Esquerda / Direita.

Aqui está a alternância de guias para o MacVim (insira .gvimrc com o restante acima):

:macm Window.Select\ Previous\ Tab key=<D-S-Left>
:macm Window.Select\ Next\ Tab key=<D-S-Right>

1

plugin do vim unimpaired.vim [e and] e


2
Parece exagero, acho que você realmente não precisa de um plugin completo para isso. 2 linhas em um vimrc faz exatamente o que o OP quer
Daniel Gratzer

1

Quando você bate comando :help moveem vim, aqui está o resultado:

:[range]m[ove] {address} *:m* *:mo* *:move* *E134* Move the lines given by [range] to below the line given by {address}.

Por exemplo: Mova a linha atual uma linha para baixo => :m+1 .

Por exemplo: Mova a linha com o número 100 abaixo da linha com o número 80 => :100 m 80 .

Por exemplo: Mova a linha com o número 100 abaixo da linha com o número 200 => :100 m 200 .

Por exemplo: Mova as linhas com o número dentro de [100, 120] abaixo da linha com o número 200 => :100,120 m 200 .


1

Aqui está uma solução que funciona na minha máquina : MacBook Pro executando o VIM 8.1

Esses comandos não excluirão suas linhas na parte superior ou inferior do seu buffer.

Usar os símbolos reais que Alt-J e Alt-K emitem é uma solução alternativa para que seus códigos-chave não sejam mapeados corretamente no meu ambiente.

Jogue isso no .vimrc antigo e veja se funciona para você.

" Maps Alt-J and Alt-K to macros for moving lines up and down
" Works for modes: Normal, Insert and Visual
nnoremap ∆ :m .+1<CR>==
nnoremap ˚ :m .-2<CR>==
inoremap ∆ <Esc>:m .+1<CR>==gi
inoremap ˚ <Esc>:m .-2<CR>==gi
vnoremap ∆ :m '>+1<CR>gv=gv
vnoremap ˚ :m '<-2<CR>gv=gv


0

: m. + 1 ou: m.-2 faria se você estivesse movendo uma única linha. Aqui está o meu script para mover várias linhas. No modo visual, Alt-up / Alt-down move as linhas que contêm a seleção visual para cima / baixo em uma linha. No modo de inserção ou modo normal, Alt-up / Alt-down moverá a linha atual se nenhum prefixo de contagem for fornecido. Se houver um prefixo de contagem, Alt-up / Alt-down moverá muitas linhas a partir da linha atual para cima / baixo em uma linha.

function! MoveLines(offset) range
    let l:col = virtcol('.')
    let l:offset = str2nr(a:offset)
    exe 'silent! :' . a:firstline . ',' . a:lastline . 'm'
        \ . (l:offset > 0 ? a:lastline + l:offset : a:firstline + l:offset)
    exe 'normal ' . l:col . '|'
endf

imap <silent> <M-up> <C-O>:call MoveLines('-2')<CR>
imap <silent> <M-down> <C-O>:call MoveLines('+1')<CR>
nmap <silent> <M-up> :call MoveLines('-2')<CR>
nmap <silent> <M-down> :call MoveLines('+1')<CR>
vmap <silent> <M-up> :call MoveLines('-2')<CR>gv
vmap <silent> <M-down> :call MoveLines('+1')<CR>gv

0

Caso você queira fazer isso em várias linhas que correspondam a uma pesquisa específica:

  • Para cima: :g/Your query/ normal ddpou:g/Your query/ m -1
  • para baixo :g/Your query/ normal ddpou:g/Your query/ m +1
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.