Mueve las líneas seleccionadas hacia arriba y hacia abajo


7

He estado usando eclipse durante un tiempo y encontré algunos accesos directos muy útiles, en particular la capacidad de mover una selección rectangular de líneas hacia arriba y hacia abajo usando Alt + Arriba / Abajo. He estado buscando esta funcionalidad en emacs. Hasta ahora he encontrado el siguiente script:

(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-up] 'move-text-up)
(global-set-key [\M-down] 'move-text-down)

Esto funciona, excepto por el hecho de que esto mueve la selección . Preferiría mover todas las líneas (incluida la nueva línea final) incluidas en la selección. ¿Hay alguna manera de darse cuenta de esto también?


Una pregunta relacionada sobre stackoverflow: stackoverflow.com/q/2423834 .
Nombre

De ahí es de donde obtuve el código. Estaba preguntando nuevamente porque esa solución no era de mi agrado.
hfhc2

Respuestas:


14

drag-stuff

Consulte el drag-stuffpaquete (también disponible en Melpa).

Luego puede seleccionar una región y usar drag-stuff-up/ drag-stuff-downpara mover esa región hacia arriba / abajo.


Comportamiento alternativo al arrastrar líneas

Por defecto, los drag-stuffcomandos también arrastrarán la línea en la que pointestá activada (incluso si el punto está en la primera columna). Si desea seleccionar, digamos 2 líneas enteras haciendo C-a C-SPC C-n C-n, la selección se verá así

 line 1
▯line 2
 line 3
▮line 4 
 line 5

Tenga en cuenta que aquí tengo la intención de mover solo las líneas 2 y 3, no la línea 4 . Pero drag-stuffmoverá esa tercera línea también por defecto.

Ese era mi motivo favorito (y probablemente no se aplica a nadie más), por lo que solicité una solución al desarrollador del paquete . Aquí hay un truco que puede poner en su configuración de emacs después de requerirlo drag-stuffsi no desea este comportamiento predeterminado. El truco no moverá la línea actual SI el punto está en la columna 0 (primera columna).

;; https://github.com/kaushalmodi/.emacs.d/blob/master/setup-files/setup-drag-stuff.el
;; https://github.com/rejeep/drag-stuff.el/issues/4
(defvar modi/drag-stuff--point-adjusted nil)
(defvar modi/drag-stuff--point-mark-exchanged nil)

(defun modi/drag-stuff--adj-pt-pre-drag ()
  "If a region is selected AND the `point' is in the first column, move
back the point by one char so that it ends up on the previous line. If the
point is above the mark, exchange the point and mark temporarily."
  (when (region-active-p)
    (when (< (point) (mark)) ; selection is done starting from bottom to up
      (exchange-point-and-mark)
      (setq modi/drag-stuff--point-mark-exchanged t))
    (if (zerop (current-column))
        (progn
          (backward-char 1)
          (setq modi/drag-stuff--point-adjusted t))
      ;; If point did not end up being on the first column after the
      ;; point/mark exchange, revert that exchange.
      (when modi/drag-stuff--point-mark-exchanged
        (exchange-point-and-mark) ; restore the original point and mark loc
        (setq modi/drag-stuff--point-mark-exchanged nil)))))

(defun modi/drag-stuff--rst-pt-post-drag ()
  "Restore the `point' to where it was by forwarding it by one char after
the vertical drag is done."
  (when modi/drag-stuff--point-adjusted
    (forward-char 1)
    (setq modi/drag-stuff--point-adjusted nil))
  (when modi/drag-stuff--point-mark-exchanged
    (exchange-point-and-mark) ; restore the original point and mark loc
    (setq modi/drag-stuff--point-mark-exchanged nil)))

(add-hook 'drag-stuff-before-drag-hook #'modi/drag-stuff--adj-pt-pre-drag)
(add-hook 'drag-stuff-after-drag-hook  #'modi/drag-stuff--rst-pt-post-drag)

Demostración de cómo funcionan las líneas de arrastre antes y después del truco anterior

Antes de hackear

 line 1                                      line 1
▯line 2                                      line 5
 line 3    --(M-x drag-stuff-down)-->       ▯line 2   MOVED LINE
▮line 4                                      line 3   MOVED LINE
 line 5                                     ▮line 4   MOVED LINE

Después de hackear

 line 1                                      line 1
▯line 2                                      line 4
 line 3    --(M-x drag-stuff-down)-->       ▯line 2   MOVED LINE
▮line 4                                      line 3   MOVED LINE
 line 5                                     ▮line 5

Atajos de teclado

Para lograr el comportamiento similar a un eclipse, simplemente agregue las combinaciones de teclas apropiadas:

(global-set-key (kbd "M-<up>")   #'drag-stuff-up)
(global-set-key (kbd "M-<down>") #'drag-stuff-down)

Hm ... por molesto que sea la línea móvil 4, definitivamente es más consistente. Por ejemplo, si tuviera que mover líneas, pero su punto era EOL, el nuevo comportamiento "pirateado" parecería realmente extraño.
PythonNut

@PythonNut Sí, si el punto no estuviera en la columna 0, toda la línea se movería. Pero por eso es un truco; Se ajusta a mi flujo de trabajo. Creo en el 99% de mis selecciones de región cuando muevo líneas, mi punto está en col 0 porque tiendo a seleccionar líneas enteras ( ejemplo C-a C-SPC C-4 C-n ) o párrafos enteros ( ejemplo C-a C-SPC M-} ). Sé que la hay M-h, pero aún no me he acostumbrado a eso. ¡Imagínese lo molesto que podría ser si cada vez que terminara arrastrando mi párrafo seleccionado más la siguiente línea! :)
Kaushal Modi

¿Tu pirateo no hará lo incorrecto si el punto está al principio de la región en lugar de al final?
Harald Hanche-Olsen

@ HaraldHanche-Olsen Correcto. Sorprendentemente, nunca hice la selección de esa manera (comenzando desde abajo y subiendo) y, por lo tanto, nunca entendí este problema :). Ahora está arreglado (como pude ver por una verificación rápida). Hazme saber si esto funciona para ti. Gracias por notar esto.
Kaushal Modi

2

Lo codifiqué hace mucho tiempo, lo uso todos los días .

(defun move-line-up ()
  "Move up the current line."
  (interactive)
  (transpose-lines 1)
  (forward-line -2)
  (indent-according-to-mode))

(defun move-line-down ()
  "Move down the current line."
  (interactive)
  (forward-line 1)
  (transpose-lines 1)
  (forward-line -1)
  (indent-according-to-mode))

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

1
esto es fantástico, gracias por publicar esto, ¡no tengo idea de por qué no tienes más votos a favor!
iFunction

1

Aquí hay una manera de hacer esto usando macros de teclado:

  • C-x ( comenzar a grabar la macro.
  • C-aSPCC-pSPCC-wC-pC-y corta una línea y pégala una línea antes en el búfer.
  • C-x ) termine de grabar la macro.
  • C-x e moverá la línea hacia arriba tantas veces como la presione.

Bueno, hay una función transpose-linesque funciona de esta manera, pero esto solo funciona para líneas simples, como su sugerencia ...
hfhc2

@ hfhc2 la macro de teclado es una solución ad hoc de todos modos. Al mismo ritmo, puede hacer que se mueva más de una línea (solo agregue un argumento numérico a C-p).
wvxvw
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.