Переміщення лінії / області вгору та вниз у emacs


85

Який найпростіший спосіб перемістити вибраний регіон або лінію (якщо немає виділення) вгору або вниз в emacs? Я шукаю ту ж функціональність, що і в eclipse (обмежена M-up, M-down).


1
Я майже впевнений, що ідіома полягає в тому, щоб просто вбити те, що ви хочете перемістити, скористатися звичайними навігаційними функціями (включаючи пошук і т.д.), щоб перемістити точку, де ви хочете, щоб був код, а потім рвати. Пам'ятайте, є маса вбивств, тому вам навіть не потрібно знаходити місце відразу; Ви можете зібрати інші біти, щоб потягнути пізніше. Особисто я, як важкий користувач Emacs, не уявляю жодної ситуації, коли я волів би рухати одну лінію вручну вгору або вниз. Це просто не було б корисно.
jrockway

7
@jrockway: дякую за ваш коментар. Я думаю, що кожен має свій спосіб робити щось. Я багато працюю з затемненням і я дуже звик рухатись лініями / регіонами. Одне з чудових моментів, коли emacs дуже розширюваний, полягає в тому, що цю функцію можна легко додати.
фіковник

3
погодився. Я часто хочу пересувати рядки вгору та вниз, як ви описуєте, незалежно від того, чи перебуваю я в редакторі, який це підтримує. Або, щодо цього, незалежно від того, чи працюю я над кодом. Word підтримує це (за абзацами) за допомогою Alt + Shift + Вгору та Вниз, і я відображаю це у своїх редакторах, коли тільки можу.
гарпо

6
@Drew і jrockway: Я думаю, що задоволення меншого навряд чи є способом emacs. Переміщення рядків відбувається лише швидше, коли ви хочете перемістити їх, ніж це робить kill / yankk. І в органічному режимі курсу це також реалізовано з причини: дійсно є багато випадків, коли ви хочете перемістити рядок, переміщуючи неправильно розміщений оператор в / з блоку, це один, змінювати порядок операторів, змінювати порядок документів (див. режим), .. Багато людей використовують його в затемненні. Те, чим ви не користуєтесь, часто не пропускаєте, але корисність рухомих ліній - це факт для багатьох програмістів
Пітер,

2
Це корисно під час редагування файлів git-rebase, що містять списки комітів, які ви можете змінити.
Ken Williams

Відповіді:


47

Рядок можна переміщати за допомогою ліній транспонування, прив'язаних до C-x C-t. Не знаю про регіони.

Я знайшов цей фрагмент elisp, який робить те, що ви хочете, за винятком того, що вам потрібно змінити прив'язки.

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

Дякую за фрагмент. Це саме те, що я шукав!
fikovnik

Є невелика проблема як із цим рішенням, так і з майже ідентичним, розміщеним @sanityinc: якщо я використовую визначений ярлик для переміщення регіону вгору та / або вниз, то негайно використовую ярлик для скасування (C-_ за замовчуванням) регіон просто зникає, і з'являється повідомлення "Скасувати в регіоні!" відображається в області луни. Інша команда скасування видає повідомлення "Немає додаткової інформації про скасування для регіону". Однак якщо я переміщу курсор, а потім виконую команду скасувати, скасування знову починає працювати. Порівняно незначне роздратування, але всі інші команди Emacs можна негайно скасувати, не вдаючись до хитрості.
Teemu Leisti

1
Це точно правильний спосіб зробити це. Рядок транспонування за замовчуванням у Emacs НЕ правильна функціональність, оскільки курсор переходить до наступного рядка після транспонування, що ускладнює повторення його поетапно.
mythicalcoder

51

Оновлення: Встановіть move-textпакет від Marmalade або MELPA, щоб отримати такий код.

Ось те, що я використовую, що працює як в регіонах, так і в окремих рядках:

(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
Я додав це до EmacsWiki для вас, це тут: emacswiki.org/emacs/MoveText
ocodo

Дякую! Я думаю, що там уже був код ( emacswiki.org/emacs/basic-edit-toolkit.el ), але людям буде легше знайти цю нову сторінку.
sanityinc

Ах, круто, я цього раніше не бачив, схоже, набір інструментів для базового редагування міг би використовувати очищення.
ocodo

1
@mcb Так, будь-який пакет ELPA можна встановити через el-get. (Зверніть увагу, що сьогодні навіть автор el-get рухається до package.el.) Для вибору повних рядків я б запропонував написати окрему функцію, яка швидко вибирає або поточний рядок, або розширює поточну область, включаючи цілі рядки. Потім просто викликайте цю функцію перед функціями переміщення тексту.
sanityinc

1
@sanityinc Дякую за це. У мене щойно був міні-Emacs 'O', коли я його вперше використав. Хороший матеріал!
Й.С.

31

Вам слід спробувати drag-stuff!

Він працює точно так само, як eclipse Alt+ Up/ Downдля окремих рядків, а також для вибраних ліній регіону!

На додаток до цього він дозволяє переміщати слова за допомогою Alt+ Left/ Right
Це саме те, що ви шукаєте! І він навіть доступний у репозиторіях ELPA !

Інші рішення ніколи не працювали для мене. Деякі з них глючили (транспонували лінії, змінюючи порядок, wtf?), А деякі рухалися в точно обраній області, залишаючи невиділені частини ліній на своїх позиціях. Але drag-stuffпрацює точно так само, як у затемненні!

І навіть більше! Ви можете спробувати вибрати регіон і скористатися Alt+ Left/ Right! Це перемістить вибрану область на один символ ліворуч або праворуч. Дивовижний!

Щоб увімкнути його глобально, просто запустіть це:

(drag-stuff-global-mode)

Він дійсно повинен мати необов’язкове переміщення вліво та вправо, оскільки воно замінює стандартну карту клавіш emacs за замовчуванням.
ocodo

@Slomojo можливо! Чому б вам не запропонувати це на emacswiki? :)
Алекс-Даніель Якименко-А.

1
Раніше я цим користувався, зараз використовую smart-shift. Мені подобається, що це DWIM для горизонтального руху.
jpkotta

1
Потребує (drag-stuff-define-keys)файлу init до того, як клавіатури почнуть працювати. Це пояснюється у цьому github: github.com/rejeep/drag-stuff.el
agent18

11

Я написав пару інтерактивних функцій для переміщення рядків вгору / вниз:

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

Клавіатури пов’язані зі стилем IntelliJ IDEA, але ви можете використовувати все, що завгодно. Ймовірно, мені слід реалізувати деякі функції, що діють і на регіони.


5

Ось мій фрагмент для переміщення поточного рядка або рядків, що охоплюються активною областю. Він поважає положення курсору та виділену область. І це не буде розривати лінії, коли регіон не починається / не закінчується на кордоні лінії. (Він натхненний затемненням; я знайшов спосіб затемнення більш зручним, ніж `` транспонування ліній ''.)

;; 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
Для мене пакет перенесення тексту був порушений. Ваше рішення ідеально працює в моїй системі. Дякую за це!
Rune Kaagaard


1

Вбудованого не існує. Ви можете використовувати лінії транспонування (Cx Ct), але ви не можете використовувати їх повторно. Подивіться на функції на http://www.schuerig.de/michael/blog/index.php/2009/01/16/line-movement-for-emacs/ .

Це повинно бути легко адаптувати до регіонів.


1
До речі, якщо ви все-таки вирішите використовувати зв’язаний фрагмент, ви можете змінити оператор let на (let ((col (current-column)) (line-move-visual nil)), інакше ви отримуєте забавну поведінку із загорнутими рядками .
Лео Alekseyev

1

transpose-paragraphФункція може допомогти вам.

Можливо, ви також захочете поглянути на розділ транспонування в посібнику Emacs. По суті:

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

Для цього я використовую пакет smart-shift (у Мельпі). За замовчуванням він перев'язується C-C <arrow>для переміщення лінії або регіону. Він рухається горизонтально на величину, залежну від основного режиму (наприклад, c-basic-offset або python-indent-offset). Працює і з регіонами.

;; binds C-C <arrows>
(when (require 'smart-shift nil 'noerror)
  (global-smart-shift-mode 1))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.