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