Sposta linea / regione su e giù in emacs


85

Qual è il modo più semplice per spostare la regione o la linea selezionata (se non c'è selezione) su o giù in emacs? Sto cercando la stessa funzionalità di eclipse (limitata a M-up, M-down).


1
Sono abbastanza sicuro che l'idioma sia semplicemente uccidere ciò che vuoi spostare, usare le solite funzioni di navigazione (incluso isearch, ecc.) Per spostare il punto in cui vuoi che sia il codice, e poi strattonare. C'è una pila di uccisioni, ricorda, quindi non devi nemmeno trovare immediatamente la posizione; puoi raccogliere altri pezzi da strappare in seguito. Personalmente, come utente pesante di Emacs, non riesco a immaginare nessuna situazione in cui preferirei spostare manualmente una singola riga su o giù. Semplicemente non sarebbe utile.
jrockway

7
@jrockway: grazie per il tuo commento. Penso che ognuno abbia il suo modo di fare le cose. Lavoro molto con eclipse e sono molto abituato a spostare linee / regioni. Uno dei vantaggi di avere emacs molto estensibile è che questa funzionalità può essere facilmente aggiunta.
fikovnik

3
concordato. Spesso desidero spostare le righe su e giù mentre descrivi, indipendentemente dal fatto che io sia in un editor che lo supporta. Oppure, se è per questo, se sto lavorando o meno sul codice. Word lo supporta (per paragrafo) con Alt + Maiusc + Su e Giù, e lo mappo nei miei editor ogni volta che posso.
arpione

6
@ Drew e jrockway: penso che accontentarsi di meno non sia certo il modo di emacs. Spostare le linee è solo più veloce quando vuoi spostarle rispetto a kill / yankk. E naturalmente anche la modalità org l'ha implementata per una ragione: in effetti ci sono molti casi in cui si desidera spostare una riga, spostare un'istruzione inserita in modo sbagliato all'interno / all'esterno di un ambito di blocco è uno, modificare l'ordine delle istruzioni, modificare l'ordine dei documenti (vedere org- modalità), .. Molte persone lo usano in Eclipse. Ciò che non usi, spesso non ti perdi, ma l'utilità di spostare le linee è un dato di fatto per un sacco di programmatori
Peter

2
Ciò è utile quando si modificano i file git-rebase, contenenti elenchi di commit che potresti voler riordinare.
Ken Williams

Risposte:


47

Una linea può essere spostata usando le linee di trasposizione legate a C-x C-t. Non so delle regioni però.

Ho trovato questo frammento di elisp che fa quello che vuoi, tranne per il fatto che devi cambiare le associazioni.

(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)

Grazie per lo snippet. Questo e 'esattamente quello che stavo cercando!
fikovnik

C'è un piccolo problema sia con questa soluzione, sia con quella quasi identica pubblicata da @sanityinc: se uso la scorciatoia definita per spostare una regione su e / o giù, allora uso immediatamente la scorciatoia per annullare (C-_ per impostazione predefinita), la regione scompare e il messaggio "Annulla nella regione!" viene visualizzato nell'area dell'eco. Un altro comando di annullamento restituisce il messaggio "Nessuna ulteriore informazione di annullamento per la regione". Tuttavia, se sposto il cursore, quindi comando Annulla, l'annullamento riprende a funzionare. Un'irritazione relativamente minore, ma tutti gli altri comandi di Emacs possono essere annullati immediatamente senza ricorrere a un trucco.
Teemu Leisti

1
Questo è esattamente il modo giusto per farlo. La linea di trasposizione predefinita in Emacs NON è una funzionalità corretta poiché il cursore si sposta alla riga successiva dopo la trasposizione rendendo difficile ripeterla passo dopo passo.
mythicalcoder

51

Aggiornamento: installa il move-textpacchetto da Marmalade o MELPA per ottenere il seguente codice.

Ecco cosa uso, che funziona su entrambe le regioni e singole linee:

(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)

1
Ho aggiunto questo a EmacsWiki per te, è qui: emacswiki.org/emacs/MoveText
ocodo

Grazie! Penso che il codice fosse già lì ( emacswiki.org/emacs/basic-edit-toolkit.el ) ma questa nuova pagina sarà più facile da trovare per le persone.
sanityinc

Ahh, fantastico, non l'ho mai visto prima, sembra che basic-edit-toolkit possa usare una pulizia.
ocodo

1
@mcb Sì, qualsiasi pacchetto ELPA può essere installato tramite el-get. (Notare che anche l'autore di el-get si sta muovendo verso package.el in questi giorni.) Per selezionare le righe complete, suggerirei di scrivere una funzione separata che seleziona rapidamente la riga corrente o estende la regione corrente per includere righe complete. Quindi invoca quella funzione prima delle funzioni di testo di spostamento.
sanityinc

1
@sanityinc Grazie per questo. Ho appena avuto un mini Emacs 'O' la prima volta che l'ho usato. Roba buona!
JS.

31

Dovresti provare drag-stuff!

Funziona esattamente come eclipse Alt+ Up/ Downper linee singole, così come per linee regionali selezionate!

Inoltre ti permette di spostare le parole con Alt+ Left/ Right
Questo è esattamente quello che stai cercando! Ed è anche disponibile presso i repository ELPA !

Altre soluzioni non hanno mai funzionato per me. Alcuni di loro erano difettosi (trasponevano le linee mentre cambiavano il loro ordine, wtf?) E alcuni di loro stavano spostando esattamente la regione selezionata, lasciando parti non selezionate delle linee nelle loro posizioni. Ma drag-stufffunziona esattamente come in Eclipse!

E anche di più! Puoi provare a selezionare una regione e utilizzare Alt+ Left/ Right! Questo trasporrà la regione selezionata di un carattere a sinistra oa destra. Sorprendente!

Per abilitarlo a livello globale, esegui semplicemente questo:

(drag-stuff-global-mode)

Ha davvero bisogno di avere il movimento sinistro e destro opzionale, poiché sovrascrive la mappa dei tasti predefinita di emacs.
ocodo

@Slomojo forse! Perché non lo suggerisci su emacswiki? :)
Aleks-Daniel Jakimenko-A.

1
Usavo questo, ora sto usando il cambio intelligente. Mi piace che sia DWIM per il movimento orizzontale.
jpkotta

1
Necessità (drag-stuff-define-keys)nel file init prima che le associazioni di tasti inizino a funzionare. Questo è spiegato in questo github: github.com/rejeep/drag-stuff.el
agent18

11

Ho scritto un paio di funzioni interattive per spostare le righe su / giù:

;; 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)

Le combinazioni di tasti sono in stile IntelliJ IDEA, ma puoi usare tutto ciò che desideri. Probabilmente dovrei implementare alcune funzioni che operano anche sulle regioni.


5

Ecco il mio frammento per spostare la linea corrente o le linee attraversate dalla regione attiva. Rispetta la posizione del cursore e la regione evidenziata. E non interromperà le linee quando la regione non inizia / finisce al bordo della linea. (È ispirato da eclipse; ho trovato il modo eclipse più conveniente di "trasponi-linee".)

;; 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)))

1
Per me il pacchetto mossa-testo era rotto. La tua soluzione funziona perfettamente sul mio sistema. Grazie per quello!
Rune Kaagaard


1

Non c'è built-in. Puoi usare le linee di trasposizione (Cx Ct) ma non puoi usarlo ripetutamente. Guarda le funzioni su http://www.schuerig.de/michael/blog/index.php/2009/01/16/line-movement-for-emacs/ .

Dovrebbe essere facile adattarlo anche alle regioni.


1
A proposito, se decidi di utilizzare lo snippet collegato, potresti voler cambiare l'istruzione let in (let ((col (current-column)) (line-move-visual nil)), altrimenti ottieni un comportamento divertente con linee a capo .
Leo Alekseyev

1

La transpose-paragraphfunzione potrebbe aiutarti.

Potresti anche voler dare un'occhiata alla sezione di trasposizione nel manuale di Emacs. Essenzialmente:

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).

0

Per questo utilizzo il pacchetto smart-shift (nel Melpa). Per impostazione predefinita, si riassocia C-C <arrow>per spostare una linea o una regione. Si sposta orizzontalmente di una quantità specifica della modalità principale (ad esempio, offset di base c o offset di rientro di python). Funziona anche sulle regioni.

;; binds C-C <arrows>
(when (require 'smart-shift nil 'noerror)
  (global-smart-shift-mode 1))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.