Przesuwaj całą linię w górę iw dół w Vimie


311

W Notepad ++ mogę użyć Ctrl+ Shift+ Up/, Downaby przesunąć bieżącą linię w górę lub w dół. Czy w Vimie jest podobne polecenie? Przeglądałem niekończące się przewodniki, ale nic nie znalazłem.

Jeśli nie, jak mogę powiązać działanie z tą kombinacją klawiszy?

Edycja: Odpowiedź Mykoli działa dla wszystkich linii, z wyjątkiem tych na początku i na końcu bufora. Przesunięcie pierwszej linii w górę lub dolnej linii w dół usuwa linię, a podczas przesuwania dolnej linii w górę, początkowo przeskakuje o dwa pola, jak pionek! Czy ktoś może zaoferować jakieś udoskonalenia?


Nie miałem wyboru, jak wdrożyć rozwiązanie skryptowe. Mam nadzieję, że jest czysty i dostosowany do twoich potrzeb.
Mykola Golubyev

31
Dla was,
guns

Nie jestem pewien, dlaczego potrzebujesz skryptu, przykłady artykułów z Wikii działają. Opublikowałem poniżej uproszczonej wersji, ponieważ przykład Wikii z 3 różnymi trybami mapowania może być dość zniechęcający (i naprawdę nie konieczny. Jeśli używasz tylko mapowania wyboru bloku, możesz po prostu pamiętać, aby zablokować wybór (Shift V) i użyć te skróty (patrz moja odpowiedź poniżej).

Screencast na ten temat: vimcasts.org/e/26
glts

Również na vi SE
cfi

Odpowiedzi:


81

Umieść następujące polecenie w pliku .vimrc, aby wykonać zadanie

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

Zniknięcie linii wygląda jak błąd Vima. Robię hack, aby tego uniknąć. Prawdopodobnie istnieje jakieś bardziej dokładne rozwiązanie.

Aktualizacja

Istnieje wiele niewyjaśnionych trudności związanych z używaniem kombinacji Vima. Są to brakujące linie i dodatkowe skakanie.

Oto rozwiązanie skryptowe, które można umieścić w .vimrc lub ~ / .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
Musiałem zmienić na <cj> i <ck>, ponieważ wydaje się, że występuje konflikt z moją konfiguracją, ale fantastyczna odpowiedź!
Wielkie

1
Dzięki, świetne dla git rebase - interaktywne. Musiał powiązać z <cj> i <ck>. cs-up / down nie działał.
Lari Hotari,

2
Jeśli ktoś chce inny skrót: c służy do kontroli, A do alt, używam <A-up>jak w eclipse.
sty

1
Znikająca linia nie jest błędem Vima! Wynika to raczej z działania ddkPw ostatnim wierszu dokumentu. Wypróbuj sam na pliku 3-liniowym. Aby uniknąć takich problemów, użyj raczej łatwiejszych :mmapowań podanych na wiki Vima .
Serge Stroobandt,

1
Zmieniłem ostatnie linie na <A-up>i, <A-down>ale kiedy próbuję tych klawiszy w vimie w trybie wizualnym uruchomionym w tmux, zaznaczenie znika, a kursor przesuwa się w górę; bez przesuwania wybranych linii.
Konrad

574

Jeśli chcę zamienić jedną linię z linią powyżej, zwykle wykonuję następujące czynności

ddkP

Wyjaśnienie

  • dd usunie linię i doda ją do rejestru domyślnego.
  • k przesunie się w górę linii (j przesunie się w dół linii)
  • P wklei powyżej bieżącej linii

5
I zawsze jest dostępny. :-)
Henno,

71
I ddpprzesunąć linię w dół (usuń linię i wklej poniżej bieżącej linii)
bcoughlan

2
Aby przenieść wiązkę linii, powiedz 4, 4 dda następnie przejdź do, powiedz 2 linii, a następnie :2tam, gdzie chcesz wkleić i P.
Guru

2
@Guru powinno być 4ddzamiast 4 dd?
Patrick McDonald,

3
I dd<number>jpaby przenieść bieżącą linię <numer> +1 linii w dół. I dd<number>kpaby przenieść bieżącą linię <numer> linii w górę.
egelev

230

Zakładając, że kursor znajduje się na linii, którą chcesz przenieść.

Poruszanie się w górę i w dół: :mdo ruchu

:m +1 - przesuwa się o 1 linię w dół

:m -2 - przesuń o 1 linię w górę

(Uwaga: możesz zastąpić +1 dowolnymi liczbami w zależności od tego, ile linii chcesz przesunąć w górę lub w dół, tj. +2 przesunąłby go w dół o 2 linie, -3 przesunąłby go w górę o 2 linie)

Aby przejść do określonej linii

:set number - wyświetlaj linie numeryczne (łatwiej zobaczyć, gdzie go przenosisz)

:m 3 - przesuń linię po trzeciej linii (zamień 3 na dowolną linię, którą chcesz)

Przenoszenie wielu linii:

V(tj. Shift- V) i przesuwaj Courser w górę i w dół, aby wybrać wiele linii w VIM

po wybraniu naciśnij :i uruchom powyższe polecenia m +1itp


14
dla mnie: m -2 przesuwa się tylko o jedną linię w górę.
Hermann Ingjaldsson

2
umieściłem tę metodę w moim vimrc, ale potem nie mogę powtórzyć polecenia używając „.”. czy ktoś wie dlaczego tak jest?
Hermann Ingjaldsson

Bardzo dobrze! Nie tak wygodne jak rozwiązanie w Notepad ++, ale nadal - prawdziwe i działające.
Arturas M

Hermann, przepraszam, literówka / pomyłka z mojej strony, masz rację: m -2 przeniesie ją o jedną linię w górę. Zmieniłem to. Również przepraszam za bardzo późną odpowiedź (nie jestem pewien co do części .vimrc) Pozdrawiam
Serg

2
Przenoszenie za pomocą polecenia działa, jeśli chcesz go używać w powiązaniach, takich jak te z Notepad ++, o które poprosił OP, musisz wprowadzić następujące polecenie w .vimrc:noremap <c-s-up> :m -2<CR> noremap <c-s-down> :m +1<CR>
kap

74

Przenieś linię w górę: ddkP

Przesuń linię w dół: ddp


1
posiadanie tego w vimrc uniemożliwia powtórzenie polecenia za pomocą „.”
Hermann Ingjaldsson

@HermannIngjaldsson, przypuszczam, że możesz zawinąć polecenie w funkcję, jeśli chcesz mieć możliwość .powtarzania.
jacobsimeon

Tak, .. i jeśli chcesz przesunąć więcej linii, możesz użyć V zamiast dd.
sensorario

15

To działało dla mnie:

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

BTW, jeśli chcesz użyć ALT + some_key, a twój terminal (urxvt robi to) odmawia spełnienia, powinieneś wpisać coś takiego w swoim .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

gdzie ^] to pojedynczy znak reprezentujący klawisz ALT. Aby wprowadzić ten znak, użyj C+v, Escw Vimie ( C+q, Escw systemie Windows).


1
To dobrze, używając wbudowanego polecenia Vima do przesuwania linii. Bardziej prawdopodobne jest zachowanie się w przypadku cofnięcia lub błędu.
Josh Lee

1
Dlaczego nie wpiszesz Esc jako <kbd>Cv<kbd/> <kbd> Esc </kbd> (linux). Windows zastępuje <kbd> Cv <kbd /> przez <kbd> Cq <kbd /> na całej planszy, ale ma taki sam sposób na wprowadzanie dowolnego specjalnego klawisza w trybie wstawiania / poleceń
patrz

@sehe: Dzięki, nie wiedziałem o tym wtedy. Zaktualizowałem odpowiedź.
emil.p.stanchev

1
@RafaelRinaldi, [niv]noremapsą remapami dla normalnych trybów | wstawiania | wizualnego. <CR>jest Carriage Return( Enterklucz).
vp_arth

1
@vp_arth Nie, nie ma
patrz

13

dodaj następujące do pliku ~ / .vimrc (upewnij się, że nie masz mapowania dla n, m)

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

teraz naciśnięcie nklawisza spowoduje przesunięcie linii w dół i mprzesunięcie linii w górę.


12

W trybie poleceń ustaw kursor na linii, którą chcesz przesunąć w dół, a następnie

ddp

Objaśnienie: dd usuwa bieżącą linię do bufora ogólnego p przywraca ją PO pozycji kursora lub, w przypadku całych linii, jedną linię poniżej.

Istnieje wiele nieporozumień dotyczących poleceń p i P w wielu dokumentach. W rzeczywistości p wkleja się PO kursorze, a P AT kursor.


8

Po prostu dodaj ten kod do .vimrc (lub .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
Zgodnie z vim.wikia.com/wiki / ... patrz tam, aby uzyskać pełne wyjaśnienie.
Serge Stroobandt,

Na komputerze Mac <A-something> nie działa, nie wiem dlaczego. Zamieniłem na <Cj> <Ck> i działa dobrze, używając klawisza Ctrl.
Cédric NICOLAS



3

Na końcu mojego pliku .vimrc umieszczam:

noremap H ddkkp
noremap N ddp

Zatem teraz „H” i „N” przesuwają linię odpowiednio w górę i w dół.


H i N są w rzeczywistości przydatnymi poleceniami (H przesuwa kursor na górę i początek ekranu, N przechodzi do poprzedniego wyniku wyszukiwania), więc
zalecałbym

Widzę. Skonfigurowałem je również. Aby przejść na górę ekranu, naciskam a, a następnie w górę. W moich książkach zawsze znaczy wszystko. Aby przeszukać poprzednie wyniki, wciskam f, a następnie w lewo. F oznacza skrót. Ale dla tych, którzy tego nie mają, to tak ... to jest ważne.
Hermann Ingjaldsson,

1

Oto uproszczona wersja dla MacVim, wykorzystująca przykłady artykułów z Wikii (por. Link z komentarza 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

Używam tylko wariantu wyboru bloku, ponieważ wystarczy Shift-V, aby wybrać bieżącą linię, i opcjonalnie kursor w górę / w dół, aby wybrać więcej linii.

Zgodnie z powyższymi skrótami naciśnięcie Cmd-Shift-Up / Down spowoduje przesunięcie wyboru bloku w górę / w dół. „D” to klawisz Command w MacVimie, w systemie Windows spróbuj „C” (Control) lub „A” (Alt) (np. <CAf> to Control Alt f).

Artykuł Wikii dodaje do nich „= gv”, co powoduje dostosowanie wcięcia bloku po ruchu na podstawie otaczającego tekstu. Jest to mylące, więc usunąłem go i dodałem skróty do szybkiego wcięcia zaznaczenia.

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

Uwaga, to samo można zrobić z << i >>, ale wybór zostałby utracony, więc powyższe skróty pozwalają na wielokrotne wcięcie i nadal przesuwają blok, ponieważ wybór jest zachowany.

Mój MacVim jest skonfigurowany do przełączania Tab z Cmd-Shift-Left / Right, więc użyłem Cmd-Alt-Left / Right.

Oto przełączanie Tab dla MacVim (wstaw .gvimrc z resztą powyżej):

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

1

vim plugin unimpaired.vim [e i] e


2
To wydaje się przesadą, nie sądzę, że naprawdę potrzebujesz do tego pełnej wtyczki. 2 linie w vimrc robi dokładnie to, czego chce OP
Daniel Gratzer

1

Kiedy trafisz polecenia :help movew vim, oto wynik:

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

Np .: Przenieś bieżącą linię o jedną linię w dół => :m+1 .

Np .: Przesuń linię o numerze 100 poniżej linii o numerze 80 => :100 m 80 .

Np .: Przesuń linię o numerze 100 poniżej linii o numerze 200 => :100 m 200 .

Np .: Przenieś linie o numerze w obrębie [100, 120] poniżej linii o numerze 200 => :100,120 m 200 .


1

Oto rozwiązanie, które działa na moim komputerze : MacBook Pro z systemem VIM 8.1

Te polecenia nie usuwają twoich linii na górze lub na dole bufora.

Używanie rzeczywistych symboli, które generują Alt-J i Alt-K, jest obejściem problemu dla ich kodów kluczy niepoprawnie mapowanych w moim środowisku.

Rzuć to w starym .vimrc i sprawdź, czy działa dla ciebie.

" 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 lub: m.-2 zrobi się, jeśli przesuniesz jedną linię. Oto mój skrypt do przenoszenia wielu linii. W trybie wizualnym Alt-up / Alt-down przesuwa linie zawierające zaznaczenie wizualne w górę / w dół o jedną linię. W trybie wstawiania lub w trybie normalnym Alt-up / Alt-down przesunie bieżącą linię, jeśli nie podano prefiksu zliczania. Jeśli istnieje prefiks liczenia, Alt-up / Alt-down przesunie tyle linii, zaczynając od bieżącej linii góra / dół o jedną linię.

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

Jeśli chcesz to zrobić w wielu wierszach pasujących do określonego wyszukiwania:

  • W górę: :g/Your query/ normal ddplub:g/Your query/ m -1
  • W dół :g/Your query/ normal ddplub:g/Your query/ m +1
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.