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