Przenieś linię / region w górę iw dół w emacsie

85

Jaki jest najłatwiejszy sposób na przesunięcie wybranego regionu lub linii (jeśli nie ma zaznaczenia) w górę lub w dół w emacsie? Szukam tej samej funkcjonalności, co w eclipse (ograniczone do M-up, M-down).

fikovnik
źródło
1
Jestem prawie pewien, że idiom to po prostu zabić to, co chcesz przenieść, użyć zwykłych funkcji nawigacyjnych (w tym isearch itp.), Aby przesunąć punkt, w którym chcesz, aby kod był, a następnie szarpnąć. Pamiętaj, że jest tam stos zabójstw, więc nie musisz nawet od razu szukać miejsca; możesz zebrać inne bity do szarpnięcia później. Osobiście, jako ciężki użytkownik Emacsa, nie wyobrażam sobie sytuacji, w której wolałbym ręcznie przesuwać pojedynczą linię w górę lub w dół. Po prostu nie byłoby to przydatne.
jrockway
7
@jrockway: dziękuję za komentarz. Myślę, że każdy ma swój własny sposób postępowania. Dużo pracuję z zaćmieniem i jestem przyzwyczajony do przesuwania linii / regionów. Jedną z wielkich zalet emacsa, który jest bardzo rozszerzalny, jest to, że tę funkcjonalność można łatwo dodać.
fikovnik
3
Zgoda. Często chcę przesuwać linie w górę iw dół, jak opisujesz, niezależnie od tego, czy pracuję w edytorze, który to obsługuje. Albo, jeśli o to chodzi, czy pracuję nad kodem. Word obsługuje to (akapitami) za pomocą Alt + Shift + Góra i Dół i mapuję to w moich edytorach, kiedy tylko mogę.
harpo
6
@Drew i jrockway: Myślę, że zadowolenie się mniej jest raczej sposobem emacsa. Przesuwanie linii jest po prostu szybsze, gdy chcesz je przesunąć, niż zabijanie / szarpanie. Oczywiście tryb org ma go również zaimplementowany z jakiegoś powodu: rzeczywiście jest wiele przypadków, w których chcesz przenieść linię, przenieść niewłaściwie umieszczoną instrukcję do / z zakresu blokowego, zmienić kolejność instrukcji, zmienić kolejność dokumentów (patrz org- mode), .. Wiele osób używa go podczas zaćmienia. Co nie używasz, często nie brakuje, ale przydatność ruchomych linii jest fakt dla całej partii programistów
Peter
2
Jest to przydatne podczas edycji plików git-rebase zawierających listy zatwierdzeń, które możesz chcieć zmienić.
Ken Williams,

Odpowiedzi:

47

Linię można przesuwać za pomocą linii transpozycji powiązanych z C-x C-t. Nie wiem jednak o regionach.

Znalazłem ten fragment kodu elisp, który robi to, co chcesz, z wyjątkiem tego, że musisz zmienić powiązania.

(defun move-text-internal (arg)
   (cond
    ((and mark-active transient-mark-mode)
     (if (> (point) (mark))
            (exchange-point-and-mark))
     (let ((column (current-column))
              (text (delete-and-extract-region (point) (mark))))
       (forward-line arg)
       (move-to-column column t)
       (set-mark (point))
       (insert text)
       (exchange-point-and-mark)
       (setq deactivate-mark nil)))
    (t
     (beginning-of-line)
     (when (or (> arg 0) (not (bobp)))
       (forward-line)
       (when (or (< arg 0) (not (eobp)))
            (transpose-lines arg))
       (forward-line -1)))))

(defun move-text-down (arg)
   "Move region (transient-mark-mode active) or current line
  arg lines down."
   (interactive "*p")
   (move-text-internal arg))

(defun move-text-up (arg)
   "Move region (transient-mark-mode active) or current line
  arg lines up."
   (interactive "*p")
   (move-text-internal (- arg)))

(global-set-key [\M-\S-up] 'move-text-up)
(global-set-key [\M-\S-down] 'move-text-down)
Martin Wickman
źródło
Dzięki za fragment. To jest dokładnie to, czego szukałem!
fikovnik
Jest mały problem zarówno z tym rozwiązaniem, jak i prawie identycznym opublikowanym przez @sanityinc: jeśli użyję zdefiniowanego skrótu do przesunięcia regionu w górę i / lub w dół, natychmiast użyj skrótu do cofania (domyślnie C-_) region po prostu znika, a komunikat „Cofnij w regionie!” jest wyświetlany w obszarze echa. Kolejne polecenie cofania powoduje wyświetlenie komunikatu „Nie można już cofnąć informacji dla regionu”. Jeśli jednak przesunę kursor, a następnie polecę cofnij, cofanie zaczyna działać ponownie. Stosunkowo niewielka irytacja, ale wszystkie inne polecenia Emacsa można natychmiast cofnąć bez uciekania się do sztuczek.
Teemu Leisti,
1
To jest właśnie właściwy sposób. Domyślna linia transpozycji w Emacsie NIE jest poprawną funkcjonalnością, ponieważ po transpozycji kursor przesuwa się do następnej linii, co utrudnia powtarzanie jej krok po kroku.
mythicalcoder
51

Aktualizacja: Zainstaluj move-textpakiet z Marmalade lub MELPA, aby uzyskać następujący kod.

Oto czego używam, co działa w obu regionach i na poszczególnych liniach:

(defun move-text-internal (arg)
  (cond
   ((and mark-active transient-mark-mode)
    (if (> (point) (mark))
        (exchange-point-and-mark))
    (let ((column (current-column))
          (text (delete-and-extract-region (point) (mark))))
      (forward-line arg)
      (move-to-column column t)
      (set-mark (point))
      (insert text)
      (exchange-point-and-mark)
      (setq deactivate-mark nil)))
   (t
    (let ((column (current-column)))
      (beginning-of-line)
      (when (or (> arg 0) (not (bobp)))
        (forward-line)
        (when (or (< arg 0) (not (eobp)))
          (transpose-lines arg)
          (when (and (eval-when-compile
                       '(and (>= emacs-major-version 24)
                             (>= emacs-minor-version 3)))
                     (< arg 0))
            (forward-line -1)))
        (forward-line -1))
      (move-to-column column t)))))

(defun move-text-down (arg)
  "Move region (transient-mark-mode active) or current line
  arg lines down."
  (interactive "*p")
  (move-text-internal arg))

(defun move-text-up (arg)
  "Move region (transient-mark-mode active) or current line
  arg lines up."
  (interactive "*p")
  (move-text-internal (- arg)))


(global-set-key [M-S-up] 'move-text-up)
(global-set-key [M-S-down] 'move-text-down)
zdrowie psychiczne
źródło
1
Dodałem to dla Ciebie do EmacsWiki, jest tutaj: emacswiki.org/emacs/MoveText
ocodo
Dzięki! Myślę, że ten kod już tam był ( emacswiki.org/emacs/basic-edit-toolkit.el ), ale ta nowa strona będzie łatwiejsza do znalezienia.
sanityinc
Ahh, fajnie, nie widziałem tego wcześniej, wygląda na to, że podstawowy zestaw narzędzi do edycji przydałby się porządek.
ocodo
1
@mcb Tak, każdy pakiet ELPA można zainstalować przez el-get. (Zauważ, że obecnie nawet autor el-get zmierza w kierunku package.el). Aby wybrać pełne linie, sugerowałbym napisanie oddzielnej funkcji, która szybko wybiera albo bieżącą linię, albo rozszerza bieżący region, aby obejmował całe linie. Następnie wywołaj tę funkcję przed funkcjami przenoszenia tekstu.
sanityinc
1
@sanityinc Dziękuję za to. Po raz pierwszy użyłem mini „O” Emacsa. Dobry towar!
JS.
31

Powinieneś spróbować drag-stuff!

Działa dokładnie tak samo jak zaćmienie Alt+ Up/ Downdla pojedynczych linii, jak również dla wybranych linii regionu!

Oprócz tego pozwala na przenoszenie słów za pomocą Alt+ Left/ Right
To jest dokładnie to, czego szukasz! I jest nawet dostępny w repozytoriach ELPA !

Inne rozwiązania nigdy u mnie nie działały. Niektóre z nich były błędne (transponowały linie podczas zmiany ich kolejności, wtf?), A niektóre z nich poruszały się dokładnie w wybranym regionie, pozostawiając niewybrane części linii na swoich pozycjach. Ale drag-stuffdziała dokładnie tak, jak w zaćmieniu!

I nawet więcej! Możesz spróbować wybrać region i użyć Alt+ Left/ Right! Spowoduje to transpozycję wybranego regionu o jeden znak w lewo lub w prawo. Niesamowity!

Aby włączyć to globalnie, po prostu uruchom to:

(drag-stuff-global-mode)
Aleks-Daniel Jakimenko-A.
źródło
Naprawdę musi mieć opcjonalne ruchy w lewo iw prawo, ponieważ zastępuje domyślną mapę klawiszy emacsa.
ocodo
@Slomojo może! Dlaczego nie zasugerujesz tego na emacswiki? :)
Aleks-Daniel Jakimenko-A.
1
Kiedyś tego używałem, teraz używam inteligentnej zmiany. Podoba mi się, że to DWIM do ruchu poziomego.
jpkotta
1
Potrzeby (drag-stuff-define-keys)w pliku init, zanim dowiązania klawiszy zaczną działać. Wyjaśniono to na tym githubie
agent18
11

Napisałem kilka interaktywnych funkcji do przesuwania linii w górę / w dół:

;; move line up
(defun move-line-up ()
  (interactive)
  (transpose-lines 1)
  (previous-line 2))

(global-set-key [(control shift up)] 'move-line-up)

;; move line down
(defun move-line-down ()
  (interactive)
  (next-line 1)
  (transpose-lines 1)
  (previous-line 1))

(global-set-key [(control shift down)] 'move-line-down)

Klawisze są w stylu IntelliJ IDEA, ale możesz użyć wszystkiego, co chcesz. Powinienem prawdopodobnie zaimplementować kilka funkcji, które działają również w regionach.

Bozhidar Batsov
źródło
5

Oto mój fragment do przeniesienia bieżącej linii lub linii rozciągniętych przez aktywny region. Uwzględnia pozycję kursora i podświetlony region. I nie przerywa linii, gdy region nie zaczyna się / kończy na granicy (ach) linii. (Inspiruje się zaćmieniem; zaćmienie wydaje mi się wygodniejsze niż `` linie transpozycji '').

;; move the line(s) spanned by the active region up/down (line transposing)
;; {{{
(defun move-lines (n)
  (let ((beg) (end) (keep))
    (if mark-active 
        (save-excursion
          (setq keep t)
          (setq beg (region-beginning)
                end (region-end))
          (goto-char beg)
          (setq beg (line-beginning-position))
          (goto-char end)
          (setq end (line-beginning-position 2)))
      (setq beg (line-beginning-position)
            end (line-beginning-position 2)))
    (let ((offset (if (and (mark t) 
                           (and (>= (mark t) beg)
                                (< (mark t) end)))
                      (- (point) (mark t))))
          (rewind (- end (point))))
      (goto-char (if (< n 0) beg end))
      (forward-line n)
      (insert (delete-and-extract-region beg end))
      (backward-char rewind)
      (if offset (set-mark (- (point) offset))))
    (if keep
        (setq mark-active t
              deactivate-mark nil))))

(defun move-lines-up (n)
  "move the line(s) spanned by the active region up by N lines."
  (interactive "*p")
  (move-lines (- (or n 1))))

(defun move-lines-down (n)
  "move the line(s) spanned by the active region down by N lines."
  (interactive "*p")
  (move-lines (or n 1)))
Ji Han
źródło
1
Dla mnie pakiet move-text był zepsuty. Twoje rozwiązanie działa doskonale na moim systemie. Dziękuję za to!
Rune Kaagaard
1

Nie ma wbudowanej. Możesz używać linii transpozycji (Cx Ct), ale nie możesz ich używać wielokrotnie. Spójrz na funkcje na http://www.schuerig.de/michael/blog/index.php/2009/01/16/line-movement-for-emacs/ .

Dostosowanie tego również do regionów powinno być łatwe.

Florian Thiel
źródło
1
Przy okazji, jeśli zdecydujesz się użyć połączonego fragmentu kodu, możesz chcieć zmienić instrukcję let na (let ((col (current-column)) (line-move-visual nil)), w przeciwnym razie uzyskasz zabawne zachowanie z zawijanymi liniami .
Leo Aleksiejew
1

Ta transpose-paragraphfunkcja może ci pomóc.

Możesz także zajrzeć do sekcji dotyczącej transpozycji w podręczniku Emacsa. Głównie:

C-t
Transpose two characters (transpose-chars).
M-t
Transpose two words (transpose-words).
C-M-t
Transpose two balanced expressions (transpose-sexps).
C-x C-t
Transpose two lines (transpose-lines).
Roberto Aloi
źródło
0

Używam do tego pakietu smart-shift (w Melpa). Domyślnie ponownie wiąże, C-C <arrow>aby przesunąć linię lub region. Porusza się poziomo o wielkość specyficzną dla trybu głównego (np. C-basic-offset lub python-indent-offset). Działa również w regionach.

;; binds C-C <arrows>
(when (require 'smart-shift nil 'noerror)
  (global-smart-shift-mode 1))
jpkotta
źródło