config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

move-text.el (6180B)


      1 ;;; move-text.el --- Move current line or region with M-up or M-down. -*- lexical-binding: t; -*-
      2 
      3 ;; filename: move-text.el
      4 ;; Description: Move current line or region with M-up or M-down.
      5 ;; Author: Jason Milkins <jasonm23@gmail.com>
      6 ;; Keywords: edit
      7 ;; Url: https://github.com/emacsfodder/move-text
      8 ;; Compatibility: GNU Emacs 25.1
      9 ;; Version: 2.0.10
     10 ;;
     11 ;;; This file is NOT part of GNU Emacs
     12 
     13 ;;; License
     14 ;;
     15 ;; This program is free software; you can redistribute it and/or modify
     16 ;; it under the terms of the GNU General Public License as published by
     17 ;; the Free Software Foundation; either version 3, or (at your option)
     18 ;; any later version.
     19 
     20 ;; This program is distributed in the hope that it will be useful,
     21 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     23 ;; GNU General Public License for more details.
     24 
     25 ;; You should have received a copy of the GNU General Public License
     26 ;; along with this program; see the file COPYING.  If not, write to
     27 ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
     28 ;; Floor, Boston, MA 02110-1301, USA.
     29 
     30 ;;; Commentary:
     31 ;;
     32 ;; MoveText 2.0.0 is a re-write of the old move-text and compatible with >= Emacs 25.1
     33 ;;
     34 ;; It allows you to move the current line using M-up / M-down if a
     35 ;; region is marked, it will move the region instead.
     36 ;;
     37 ;; Using the prefix (C-u *number* or META *number*) you can predefine how
     38 ;; many lines move-text will travel.
     39 ;;
     40 
     41 ;;; Installation:
     42 ;;
     43 ;; Put move-text.el to your load-path.
     44 ;; The load-path is usually ~/elisp/.
     45 ;; It's set in your ~/.emacs like this:
     46 ;; (add-to-list 'load-path (expand-file-name "~/elisp"))
     47 ;;
     48 ;; And the following to your ~/.emacs startup file.
     49 ;;
     50 ;; (require 'move-text)
     51 ;; (move-text-default-bindings)
     52 
     53 ;;; Acknowledgements:
     54 ;;
     55 ;;  Original v1.x was a Feature extracted from basic-edit-toolkit.el - by Andy Stewart (LazyCat)
     56 ;;
     57 
     58 ;;; Code:
     59 (require 'cl-lib)
     60 
     61 (defun move-text-get-region-and-prefix ()
     62     "Get the region and prefix for the `interactive' macro, without aborting.
     63 
     64 Note: `region-beginning' and `region-end' are the reason why an
     65 `interactive' macro with \"r\" will blow up with the error:
     66 
     67 \"The mark is not set now, so there is no region\"
     68 
     69 We check with `use-region-p' to avoid calling
     70 them when there's no region or it is not appropriate
     71 to act on it.
     72 
     73 We use `prefix-numeric-value' to return a number.
     74 "
     75     (list
     76      (when (use-region-p) (region-beginning)) ;; otherwise nil
     77      (when (use-region-p) (region-end))
     78      (prefix-numeric-value current-prefix-arg)))
     79 
     80 ;;;###autoload
     81 (defun move-text--total-lines ()
     82   "Convenience function to get the total lines in the buffer / or narrowed buffer."
     83   (line-number-at-pos (point-max)))
     84 
     85 ;;;###autoload
     86 (defun move-text--at-first-line-p ()
     87   "Predicate, is the point at the first line?"
     88   (= (line-number-at-pos) 1))
     89 
     90 ;;;###autoload
     91 (defun move-text--at-penultimate-line-p ()
     92   "Predicate, is the point at the penultimate line?"
     93   (= (line-number-at-pos) (1- (move-text--total-lines))))
     94 
     95 ;; save-mark-and-excursion in Emacs 25 works like save-excursion did before
     96 (eval-when-compile
     97   (when (< emacs-major-version 25)
     98     (defmacro save-mark-and-excursion (&rest body)
     99       `(save-excursion ,@body))))
    100 
    101 ;;;###autoload
    102 (defun move-text--last-line-is-just-newline ()
    103   "Predicate, is last line just a newline?"
    104   (save-mark-and-excursion
    105    (goto-char (point-max))
    106    (beginning-of-line)
    107    (= (point-max) (point))))
    108 
    109 ;;;###autoload
    110 (defun move-text--at-last-line-p ()
    111   "Predicate, is the point at the last line?"
    112   (= (line-number-at-pos) (move-text--total-lines)))
    113 
    114 ;;;###autoload
    115 (defun move-text-line-up ()
    116   "Move the current line up."
    117   (interactive)
    118     (if (move-text--at-last-line-p)
    119         (let ((target-point))
    120           (kill-whole-line)
    121           (forward-line -1)
    122           (beginning-of-line)
    123           (setq target-point (point))
    124           (yank)
    125           (unless (looking-at "\n")
    126             (newline))
    127           (goto-char target-point))
    128       (let ((col (current-column)))
    129         (progn (transpose-lines 1)
    130                (forward-line -2)
    131                (move-to-column col)))))
    132 
    133 ;;;###autoload
    134 (defun move-text-line-down ()
    135   "Move the current line down."
    136   (interactive)
    137   (unless (or
    138            (move-text--at-last-line-p)
    139            (and
    140             (move-text--last-line-is-just-newline)
    141             (move-text--at-penultimate-line-p)))
    142     (let ((col (current-column)))
    143       (forward-line 1)
    144       (transpose-lines 1)
    145       (forward-line -1)
    146       (move-to-column col))))
    147 
    148 ;;;###autoload
    149 (defun move-text-region (start end n)
    150   "Move the current region (START END) up or down by N lines."
    151   (interactive (move-text-get-region-and-prefix))
    152   (let ((line-text (delete-and-extract-region start end)))
    153     (forward-line n)
    154     (let ((start (point)))
    155       (insert line-text)
    156       (setq deactivate-mark nil)
    157       (set-mark start))))
    158 
    159 ;;;###autoload
    160 (defun move-text-region-up (start end n)
    161   "Move the current region (START END) up by N lines."
    162   (interactive (move-text-get-region-and-prefix))
    163   (move-text-region start end (- n)))
    164 
    165 ;;;###autoload
    166 (defun move-text-region-down (start end n)
    167   "Move the current region (START END) down by N lines."
    168   (interactive (move-text-get-region-and-prefix))
    169   (move-text-region start end n))
    170 
    171 ;;;###autoload
    172 (defun move-text-up (start end n)
    173   "Move the line or region (START END) up by N lines."
    174   (interactive (move-text-get-region-and-prefix))
    175   (if (not (move-text--at-first-line-p))
    176     (if (region-active-p)
    177         (move-text-region-up start end n)
    178       (cl-loop repeat n do (move-text-line-up)))))
    179 
    180 ;;;###autoload
    181 (defun move-text-down (start end n)
    182   "Move the line or region (START END) down by N lines."
    183   (interactive (move-text-get-region-and-prefix))
    184   (if (region-active-p)
    185       (move-text-region-down start end n)
    186     (cl-loop repeat n do (move-text-line-down))))
    187 
    188 ;;;###autoload
    189 (defun move-text-default-bindings ()
    190   "Bind `move-text-up' and `move-text-down' to M-up & M-down."
    191   (interactive)
    192   (global-set-key [M-down] 'move-text-down)
    193   (global-set-key [M-up]   'move-text-up))
    194 
    195 (provide 'move-text)
    196 
    197 ;;; move-text.el ends here