Di chuyển dòng / vùng lên và xuống trong emac


85

Cách dễ nhất để di chuyển vùng hoặc dòng đã chọn (nếu không có lựa chọn) lên hoặc xuống trong emacs là gì? Tôi đang tìm kiếm chức năng tương tự như trong nhật thực (giới hạn với M-up, M-down).


1
Tôi khá chắc chắn rằng thành ngữ là chỉ giết những gì bạn muốn di chuyển, sử dụng các chức năng điều hướng thông thường (bao gồm tìm kiếm, v.v.) để di chuyển điểm bạn muốn mã ở đó, và sau đó kéo. Có rất nhiều điểm giết, hãy nhớ, vì vậy bạn thậm chí không cần phải tìm vị trí ngay lập tức; bạn có thể thu thập các bit khác để kéo sau này. Cá nhân, là một người dùng Emacs nặng, tôi không thể tưởng tượng được bất kỳ tình huống nào mà tôi muốn di chuyển một dòng lên hoặc xuống theo cách thủ công. Nó sẽ không hữu ích.
jrockway

7
@jrockway: cảm ơn bình luận của bạn. Tôi nghĩ mỗi người đều có cách làm việc của riêng mình. Tôi làm việc rất nhiều với nhật thực và tôi rất quen với việc di chuyển các đường / vùng. Một trong những điều tuyệt vời của việc có emacs rất có thể mở rộng là chức năng này có thể dễ dàng được thêm vào.
fikovnik

3
đã đồng ý. Tôi thường xuyên muốn di chuyển các dòng lên xuống như bạn mô tả, cho dù tôi có đang ở trong một trình chỉnh sửa hỗ trợ nó hay không. Hoặc, đối với vấn đề đó, cho dù tôi có đang làm việc trên mã hay không. Word hỗ trợ điều này (theo đoạn) bằng Alt + Shift + Lên và Xuống và tôi lập bản đồ điều đó trong trình chỉnh sửa của mình bất cứ khi nào tôi có thể.
harpo

6
@Drew và jrockway: Tôi nghĩ rằng giải quyết cho ít hơn hầu như không phải là cách emacs. Các đường di chuyển chỉ nhanh hơn khi bạn muốn di chuyển chúng hơn là kill / yankk. Và chế độ org đã được triển khai vì một lý do: thực sự có nhiều trường hợp bạn muốn di chuyển một dòng, di chuyển một câu lệnh được đặt sai vào / ra phạm vi khối là một, thay đổi thứ tự câu lệnh, thay đổi thứ tự tài liệu (xem org- mode), .. Rất nhiều người sử dụng nó trong nhật thực. Những gì bạn không sử dụng, bạn thường không bỏ lỡ, nhưng usefullness của dòng di chuyển là một thực tế cho một toàn bộ rất nhiều lập trình viên
Peter

2
Điều này rất hữu ích khi chỉnh sửa tệp git-rebase, chứa danh sách các cam kết mà bạn có thể muốn sắp xếp lại.
Ken Williams

Câu trả lời:


47

Một dòng có thể được di chuyển bằng cách sử dụng các dòng chuyển vị liên kết với C-x C-t. Dunno về các vùng mặc dù.

Tôi tìm thấy này đoạn elisp mà những gì bạn muốn, ngoại trừ bạn cần phải thay đổi các ràng buộc.

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

Cảm ơn vì đoạn mã. Điều này thật đúng với gì mà tôi đã tìm kiếm!
fikovnik

Có một chút vấn đề với cả giải pháp này và giải pháp gần như giống hệt nhau được đăng bởi @sanityinc: nếu tôi sử dụng phím tắt đã xác định để di chuyển một vùng lên và / hoặc xuống, thì ngay lập tức sử dụng phím tắt để hoàn tác (C-_ theo mặc định), khu vực chỉ biến mất và thông báo "Hoàn tác trong khu vực!" được hiển thị trong vùng tiếng vọng. Một lệnh hoàn tác khác đưa ra thông báo "Không có thêm thông tin hoàn tác cho khu vực". Tuy nhiên, nếu tôi di chuyển con trỏ, sau đó lệnh hoàn tác, việc hoàn tác bắt đầu hoạt động trở lại. Một kích ứng tương đối nhỏ, nhưng tất cả các lệnh Emacs khác có thể được hoàn tác ngay lập tức mà không cần dùng đến thủ thuật.
Teemu Leisti

1
Đây chính xác là cách làm đúng. Dòng chuyển vị mặc định trong Emacs KHÔNG phải là chức năng chính xác vì con trỏ di chuyển đến dòng tiếp theo sau khi chuyển vị khiến việc lặp lại nó từng bước khó khăn.
mythicalcoder,

51

Cập nhật: Cài đặt move-textgói từ Marmalade hoặc MELPA để nhận mã sau.

Đây là những gì tôi sử dụng, hoạt động trên cả hai vùng và các dòng riêng lẻ:

(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
Tôi đã thêm cái này vào EmacsWiki cho bạn, nó ở đây: emacswiki.org/emacs/MoveText
ocodo

Cảm ơn! Tôi nghĩ rằng mã đó đã có ở đó ( emacswiki.org/emacs/basic-edit-toolkit.el ) nhưng trang mới này sẽ dễ dàng tìm thấy hơn cho mọi người.
sanityinc

Ahh, thú vị là tôi chưa từng thấy điều đó trước đây, có vẻ như bộ công cụ chỉnh sửa cơ bản có thể sử dụng công cụ dọn dẹp.
ocodo

1
@mcb Có, bất kỳ gói ELPA nào cũng có thể được cài đặt qua el-get. (Lưu ý rằng ngày nay, ngay cả tác giả el-get cũng đang hướng tới package.el.) Để chọn các dòng đầy đủ, tôi khuyên bạn nên viết một hàm riêng để chọn nhanh dòng hiện tại hoặc mở rộng vùng hiện tại để bao gồm các dòng đầy đủ. Sau đó, chỉ cần gọi hàm đó trước các hàm di chuyển văn bản.
sanityinc

1
@sanityinc Cảm ơn bạn vì điều này. Tôi vừa có một Emacs nhỏ 'O' lần đầu tiên tôi sử dụng nó. Đồ tốt!
JS.

31

Bạn nên thử drag-stuff!

Nó hoạt động chính xác giống như eclipse Alt+ Up/ Downcho các đường đơn, cũng như cho các đường vùng đã chọn!

Thêm vào đó, nó cho phép bạn di chuyển các từ bằng Alt+ Left/ Right
Đây chính xác là những gì bạn đang tìm kiếm! Và nó thậm chí có sẵn từ các repo ELPA !

Các giải pháp khác không bao giờ hiệu quả với tôi. Một số trong số họ đã bị lỗi (chuyển dòng trong khi thay đổi thứ tự của chúng, wtf?) Và một số trong số họ đang di chuyển chính xác vùng đã chọn, để lại các phần không được chọn của đường trên vị trí của chúng. Nhưng drag-stuffhoạt động chính xác như trong nhật thực!

Va thậm chi nhiêu hơn! Bạn có thể thử chọn một khu vực và sử dụng Alt+ Left/ Right! Thao tác này sẽ chuyển vùng đã chọn theo một ký tự sang trái hoặc phải. Kinh ngạc!

Để kích hoạt nó trên toàn cầu, chỉ cần chạy điều này:

(drag-stuff-global-mode)

Nó thực sự cần có chuyển động trái và phải tùy chọn, vì nó ghi đè sơ đồ bàn phím emacs mặc định.
ocodo

@Slomojo có lẽ! Tại sao bạn không gợi ý điều đó trên emacswiki? :)
Aleks-Daniel Jakimenko-A.

1
Tôi đã từng sử dụng cái này, bây giờ tôi đang sử dụng smart-shift. Tôi thích rằng nó DWIM cho chuyển động ngang.
jpkotta

1
Các nhu cầu (drag-stuff-define-keys)trong tệp init trước khi các liên kết khóa bắt đầu hoạt động. Điều này được giải thích trong github này: github.com/rejeep/drag-stuff.el
agent18

11

Tôi đã viết một vài hàm tương tác để di chuyển dòng lên / xuống:

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

Các keybinding là phong cách IntelliJ IDEA, nhưng bạn có thể sử dụng bất cứ thứ gì bạn muốn. Tôi có lẽ nên triển khai một số chức năng hoạt động trên các vùng.


5

Đây là đoạn mã của tôi để di chuyển dòng hiện tại hoặc các dòng được kéo dài bởi vùng hoạt động. Nó tôn trọng vị trí con trỏ và vùng được đánh dấu. Và nó sẽ không ngắt dòng khi vùng không bắt đầu / kết thúc ở (các) đường viền. (Nó được lấy cảm hứng từ nhật thực; tôi thấy cách làm nhật thực thuận tiện hơn so với 'đường chuyển vị'.)

;; 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
Đối với tôi, gói văn bản di chuyển đã bị hỏng. Giải pháp của bạn hoạt động hoàn hảo trên hệ thống của tôi. Cảm ơn vì điều đó!
Rune Kaagaard,


1

Không có cài sẵn. Bạn có thể sử dụng dòng chuyển vị (Cx Ct) nhưng bạn không thể sử dụng nó nhiều lần. Xem các chức năng trên http://www.schuerig.de/michael/blog/index.php/2009/01/16/line-movement-for-emacs/ .

Nó cũng phải dễ dàng để thích ứng với các vùng.


1
Btw, nếu bạn quyết định sử dụng đoạn mã được liên kết, bạn có thể muốn thay đổi câu lệnh let thành (let ((col (current-column)) (line-move-visual nil)), nếu không, bạn sẽ có hành vi buồn cười với các dòng được bọc .
Leo Alekseyev

1

Các transpose-paragraphchức năng có thể giúp bạn.

Bạn cũng có thể muốn xem phần chuyển vị trong hướng dẫn sử dụng Emacs. Bản chất:

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

Tôi sử dụng gói dịch vụ thông minh (trong Melpa) cho việc này. Theo mặc định, nó quay lại C-C <arrow>để di chuyển một đường hoặc vùng. Nó di chuyển theo chiều ngang bởi một lượng chính của chế độ cụ thể (ví dụ: c-basic-offset hoặc python-indent-offset). Cũng hoạt động trên các khu vực.

;; binds C-C <arrows>
(when (require 'smart-shift nil 'noerror)
  (global-smart-shift-mode 1))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.