ย้ายบรรทัด / ภูมิภาคขึ้นและลงใน emacs


85

วิธีใดที่ง่ายที่สุดในการย้ายพื้นที่หรือเส้นที่เลือก (หากไม่มีการเลือก) ขึ้นหรือลงใน emacs ฉันกำลังมองหาฟังก์ชั่นเดียวกับที่อยู่ในคราส (ขอบเขต M-up, M-down)


1
ฉันค่อนข้างแน่ใจว่าสำนวนนั้นแค่ฆ่าสิ่งที่คุณต้องการย้ายใช้ฟังก์ชันการนำทางตามปกติ (รวมถึง isearch ฯลฯ ) เพื่อย้ายจุดที่คุณต้องการให้โค้ดอยู่แล้วดึงออก มีการฆ่าเป็นจำนวนมากโปรดจำไว้ดังนั้นคุณไม่จำเป็นต้องค้นหาตำแหน่งในทันที คุณสามารถรวบรวมบิตอื่น ๆ เพื่อดึงในภายหลัง โดยส่วนตัวแล้วในฐานะผู้ใช้ Emacs จำนวนมากฉันนึกภาพไม่ออกว่าจะย้ายขึ้นหรือลงบรรทัดเดียวด้วยตนเอง มันคงไม่มีประโยชน์
jrockway

7
@jrockway: ขอบคุณสำหรับความคิดเห็นของคุณ ฉันคิดว่าทุกคนมีวิธีของตัวเองในการทำสิ่งต่างๆ ฉันทำงานกับคราสบ่อยมากและฉันคุ้นเคยกับการเคลื่อนย้ายเส้น / พื้นที่มาก สิ่งที่ยอดเยี่ยมอย่างหนึ่งของการมีอีแมคที่ขยายได้มากคือสามารถเพิ่มฟังก์ชันนี้ได้อย่างง่ายดาย
fikovnik

3
ตกลง ฉันมักต้องการเลื่อนบรรทัดขึ้นลงตามที่คุณอธิบายไม่ว่าฉันจะอยู่ในเครื่องมือแก้ไขที่รองรับหรือไม่ก็ตาม หรือสำหรับเรื่องนั้นฉันกำลังทำงานกับโค้ดหรือไม่ Word รองรับสิ่งนี้ (ตามย่อหน้า) ด้วย Alt + Shift + Up and Down และฉันแมปสิ่งนี้ในตัวแก้ไขทุกครั้งที่ทำได้
harpo

6
@Drew และ jrockway: ฉันคิดว่าการชำระเงินให้น้อยลงนั้นแทบจะไม่เป็นวิธีที่ดี การย้ายเส้นจะเร็วกว่าเมื่อคุณต้องการย้ายมากกว่า kill / yankk และ org-mode ofcourse ก็นำมาใช้ด้วยเช่นกัน: มีหลายกรณีที่คุณต้องการย้ายบรรทัดการย้ายคำสั่งที่วางผิดเข้า / ออกขอบเขตการบล็อกคือหนึ่งแก้ไขคำสั่งคำสั่งแก้ไขลำดับเอกสาร (ดู org- mode), .. คนใช้กันเยอะในคราส. สิ่งที่คุณไม่ได้ใช้คุณมักจะไม่พลาด แต่ความสมบูรณ์ของเส้นที่เคลื่อนไหวเป็นเรื่องจริงสำหรับโปรแกรมเมอร์จำนวนมาก
ปีเตอร์

2
สิ่งนี้มีประโยชน์เมื่อแก้ไขไฟล์ git-rebase ซึ่งมีรายการคอมมิตที่คุณอาจต้องการจัดลำดับใหม่
Ken Williams

คำตอบ:


47

สายสามารถเคลื่อนย้ายโดยใช้transpose สายC-x C-tผูกไว้กับ Dunno เกี่ยวกับภูมิภาคแม้ว่า

ฉันพบนี้ข้อมูลโค้ด 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
นี่คือสิ่งที่คุณกำลังมองหา! และยังมีให้บริการจากrepos ของ ELPA !

วิธีแก้ปัญหาอื่น ๆ ไม่เคยได้ผลสำหรับฉัน บางคนเป็นรถบั๊กกี้ (เปลี่ยนสายขณะเปลี่ยนลำดับ wtf?) และบางคนก็ย้ายพื้นที่ที่เลือกไว้อย่างแน่นอนโดยปล่อยให้บางส่วนของเส้นที่ไม่ได้เลือกไว้บนตำแหน่ง แต่ใช้drag-stuffงานได้เหมือนในคราส!

และมากยิ่งขึ้น! คุณสามารถลองเลือกภูมิภาคและใช้Alt+ Left/ Right! สิ่งนี้จะเปลี่ยนพื้นที่ที่เลือกทีละอักขระไปทางซ้ายหรือขวา สุดทึ่ง!

หากต้องการเปิดใช้งานทั่วโลกให้เรียกใช้สิ่งนี้:

(drag-stuff-global-mode)

จำเป็นต้องมีการเคลื่อนไหวซ้ายและขวาเป็นทางเลือกเนื่องจากมันจะลบล้างคีย์แมปเริ่มต้นของ emacs
ocodo

@Slomojo บางที! ทำไมคุณไม่แนะนำสิ่งนั้นใน emacswiki? :)
Aleks-Daniel Jakimenko-A

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
Btw ถ้าคุณตัดสินใจที่จะใช้ข้อมูลโค้ดที่เชื่อมโยงคุณอาจต้องการเปลี่ยนคำสั่ง let เป็น ((col (current-column)) (line-move-visual nil)) มิฉะนั้นคุณจะได้รับพฤติกรรมที่ตลกด้วยการตัดบรรทัด .
Leo 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 (ใน Melpa) สำหรับสิ่งนี้ โดยค่าเริ่มต้นจะย้อนC-C <arrow>กลับเพื่อย้ายเส้นหรือภูมิภาค มันเคลื่อนที่ในแนวนอนโดยจำนวนเฉพาะโหมดหลัก (เช่น c-basic-offset หรือ python-indent-offset) ทำงานในภูมิภาคด้วย

;; binds C-C <arrows>
(when (require 'smart-shift nil 'noerror)
  (global-smart-shift-mode 1))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.