commit | author | age
|
5cb5f7
|
1 |
;;; clean-aindent-mode.el --- Simple indent and unindent, trims indent white-space |
C |
2 |
|
|
3 |
;; This is free and unencumbered software released into the public domain. |
|
4 |
;; (http://unlicense.org) |
|
5 |
|
|
6 |
;; Author: peter marinov <efravia@gmail.com> |
|
7 |
;; Created: 2013-08-17 |
|
8 |
;; Last: 2015-08-16 |
|
9 |
;; Version: 1.5.0 |
|
10 |
;; Package-Version: 20171017.2043 |
|
11 |
;; License: C0 (public domain) |
|
12 |
;; URL: https://github.com/pmarinov/clean-aindent-mode |
|
13 |
;; Doc URL: http://www.emacswiki.org/emacs/CleanAutoIndent |
|
14 |
;; Keywords: indentation whitespace backspace |
|
15 |
|
|
16 |
;; This file is not part of GNU Emacs. |
|
17 |
|
|
18 |
;;; Commentary: |
|
19 |
|
|
20 |
;; === Description of the features |
|
21 |
;; 1. Extension of 'newline-and-indent' that keeps track of the last |
|
22 |
;; auto-indent operation and, if it is abandoned, would take care to |
|
23 |
;; trim down the unused white space characters. |
|
24 |
;; |
|
25 |
;; 2. Simple indent, if activated, where cursor is aligned with |
|
26 |
;; indent of the lines above. |
|
27 |
;; |
|
28 |
;; 3. Backspace Unindent. Extension of M-backspace. |
|
29 |
;; When cursor is in the indentation space of a line, or at the first |
|
30 |
;; character and you press M-backspace it will move the entire line to |
|
31 |
;; be aligned to the line above or any other that is with indentation |
|
32 |
;; smaller than the current. |
|
33 |
;; |
|
34 |
;; Detailed documentation with example situations and screenshots: |
|
35 |
;; http://www.emacswiki.org/emacs/CleanAutoIndent |
|
36 |
;; |
|
37 |
;; === To activate |
|
38 |
;; 'M-x clean-aindent-mode' |
|
39 |
;; or |
|
40 |
;; add this to your init.el: |
|
41 |
;; (clean-aindent-mode t) |
|
42 |
;; |
|
43 |
;; By default auto-indent is bound to 'C-j'. Bind it to 'RET' for most |
|
44 |
;; convenient use of the features. Add this to your init.el: |
|
45 |
;; (define-key global-map (kbd "RET") 'newline-and-indent) |
|
46 |
;; |
|
47 |
;; In case you installed the extension via MELPA, use this suggested configiration: |
|
48 |
;; |
|
49 |
;; (defun my-pkg-init() |
|
50 |
;; (electric-indent-mode -1) ; no electric indent, auto-indent is sufficient |
|
51 |
;; (clean-aindent-mode t) |
|
52 |
;; (setq clean-aindent-is-simple-indent t) |
|
53 |
;; (define-key global-map (kbd "RET") 'newline-and-indent)) |
|
54 |
;; (add-hook 'after-init-hook 'my-pkg-init) |
|
55 |
;; |
|
56 |
;; === Options |
|
57 |
;; M-x customize, search for 'auto indent', toggle to on, |
|
58 |
;; then 'Apply and Save'. |
|
59 |
;; or |
|
60 |
;; add this to your init.el: |
|
61 |
;; (set 'clean-aindent-is-simple-indent t) |
|
62 |
;; |
|
63 |
|
|
64 |
;;; Change Log: |
|
65 |
;; |
|
66 |
;; 2015-06-18, v1.5.0, pmarinov |
|
67 |
;; - Fix for a compiler warning by justbur |
|
68 |
;; - Extra comments in README |
|
69 |
;; |
|
70 |
;; 2014-06-14, pmarinov, v1.4.0 |
|
71 |
;; - Implement as an advice to 'newline-and-indent' |
|
72 |
;; |
|
73 |
;; 2014-06-01, pmarinov, v1.3.0 |
|
74 |
;; - Activate via a minor mode |
|
75 |
;; - Further cleanup of the name space |
|
76 |
;; |
|
77 |
;; 2014-05-27, pmarinov, v1.2.0 |
|
78 |
;; Changed: Move all function under the same namespace (function prefix) |
|
79 |
;; |
|
80 |
;; 2014-03-07, pmarinov, v1.1.0 |
|
81 |
;; Added: Simple auto indent feature. Configurable via M-x customize. |
|
82 |
;; |
|
83 |
;; 2013-08-31, pmarinov, v1.0.0 |
|
84 |
;; First implementation. |
|
85 |
;; |
|
86 |
|
|
87 |
|
|
88 |
(defgroup clean-aindent nil |
|
89 |
"Settings for 'clean-aindent-mode'" |
|
90 |
:group 'indent) |
|
91 |
|
|
92 |
(defcustom clean-aindent-is-simple-indent nil |
|
93 |
"Determines if indentation should use the smart language mode or simple mode" |
|
94 |
:tag "Clean auto indent is in simple mode" |
|
95 |
:group 'clean-aindent |
|
96 |
:type 'boolean) |
|
97 |
|
|
98 |
;; |
|
99 |
;; Implementation of Clean auto indent and simple indent |
|
100 |
;; |
|
101 |
|
|
102 |
(defun clean-aindent--get-indent-len() |
|
103 |
"Computes the length of the line at 'clean-aindent--last-indent." |
|
104 |
(let ((eol-pos 0)) |
|
105 |
(save-excursion |
|
106 |
(goto-char clean-aindent--last-indent) |
|
107 |
(end-of-line) |
|
108 |
(setq eol-pos (point)) |
|
109 |
(beginning-of-line) |
|
110 |
;; return ln-len = eol-pos - pos |
|
111 |
(- eol-pos (point))))) |
|
112 |
|
|
113 |
(defun clean-aindent--abandonedp() |
|
114 |
"Checks if last auto-indent position was abandoned. |
|
115 |
Verifies if cursor moved away and that the indent was left |
|
116 |
unaltered." |
|
117 |
(if (not clean-aindent--last-indent) |
|
118 |
nil |
|
119 |
;; (message "clean-aindent--last-indent %d point %d" clean-aindent--last-indent (point)) |
|
120 |
(if (= clean-aindent--last-indent (point)) |
|
121 |
nil |
|
122 |
;; Checking for indent length is to detect if something was not |
|
123 |
;; typed to alter it. Altered indent shouldn't be trimmed. |
|
124 |
(if (not (= clean-aindent--last-indent-len (clean-aindent--get-indent-len))) |
|
125 |
nil |
|
126 |
t)))) |
|
127 |
|
|
128 |
(defun clean-aindent--trim-last-point() |
|
129 |
"Deletes the whitespaces inserted at last indentation" |
|
130 |
(save-excursion |
|
131 |
(goto-char clean-aindent--last-indent) |
|
132 |
; Select the entire line |
|
133 |
(let ((s 0) |
|
134 |
(e 0)) |
|
135 |
(beginning-of-line) |
|
136 |
(setq s (point)) |
|
137 |
(end-of-line) |
|
138 |
(setq e (point)) |
|
139 |
(delete-trailing-whitespace s e) |
|
140 |
(end-of-line) |
|
141 |
(message "auto trimmed %d chars" (- e (point)))))) |
|
142 |
|
|
143 |
(defun clean-aindent--check-last-point() |
|
144 |
"Checks if last pos of auto-indent was abandoned and deletes it" |
|
145 |
(if (clean-aindent--abandonedp) |
|
146 |
(clean-aindent--trim-last-point)) |
|
147 |
;; Once we leave the position, clean the indent bookmark |
|
148 |
(if |
|
149 |
(and |
|
150 |
clean-aindent--last-indent |
|
151 |
(not (= clean-aindent--last-indent (point)))) |
|
152 |
(setq clean-aindent--last-indent nil))) |
|
153 |
|
|
154 |
(defun clean-aindent--find-indent() |
|
155 |
"Searches lines backward, finds first non-blank. Returns |
|
156 |
indentation value" |
|
157 |
(save-excursion |
|
158 |
;; Walk lines backward, until first non-blank |
|
159 |
(clean-aindent--prev-line) |
|
160 |
;; Return indentation of that line |
|
161 |
(current-indentation))) |
|
162 |
|
|
163 |
(defun clean-aindent--simple-newline-and-indent() |
|
164 |
"Simple auto indent. Indentation is based only on previous line |
|
165 |
indentation, regardless of language settings." |
|
166 |
;; First remove any trailing spaces from the current line |
|
167 |
(save-excursion |
|
168 |
(let ((s 0) |
|
169 |
(e 0)) |
|
170 |
(beginning-of-line) |
|
171 |
(setq s (point)) |
|
172 |
(end-of-line) |
|
173 |
(setq e (point)) |
|
174 |
(delete-trailing-whitespace s e) |
|
175 |
(end-of-line))) |
|
176 |
;; Insert a new line and indent |
|
177 |
(newline) |
|
178 |
(indent-to (clean-aindent--find-indent) 0)) |
|
179 |
|
|
180 |
(defadvice newline-and-indent (around clean-aindent) |
|
181 |
"Advice for newline-and-indent(), implements clean auto-indent. |
|
182 |
Removes unneeded whitespaces by keeping track of the place of the |
|
183 |
last indentation so that they can be deleted in case the indentation was |
|
184 |
abandoned." |
|
185 |
(clean-aindent--check-last-point) ;; In case of consequtive aindent calls |
|
186 |
(if clean-aindent-is-simple-indent |
|
187 |
(clean-aindent--simple-newline-and-indent) ;; Run our simple indent feature |
|
188 |
(progn |
|
189 |
ad-do-it)) ;; Run 'newline-and-indent' here |
|
190 |
(setq clean-aindent--last-indent nil) |
|
191 |
;; Make local: track on per buffer basis |
|
192 |
(make-local-variable 'clean-aindent--last-indent) |
|
193 |
;; Track position and length of the indentation |
|
194 |
(setq clean-aindent--last-indent (point)) |
|
195 |
(setq clean-aindent--last-indent-len (clean-aindent--get-indent-len)) |
|
196 |
(make-local-variable 'clean-aindent--last-indent-len)) |
|
197 |
|
|
198 |
;; |
|
199 |
;; Backspace-unindent implementation functions |
|
200 |
;; |
|
201 |
|
|
202 |
(defun clean-aindent--get-line-len() |
|
203 |
"Computes length of current line" |
|
204 |
(save-excursion |
|
205 |
(beginning-of-line nil) |
|
206 |
(let ((pos (point))) |
|
207 |
(end-of-line nil) |
|
208 |
(- (point) pos)))) |
|
209 |
|
|
210 |
(defun clean-aindent--line-emptyp() |
|
211 |
"Checks if line is empty" |
|
212 |
(save-excursion |
|
213 |
(beginning-of-line nil) |
|
214 |
(if (= (point) 1) |
|
215 |
nil |
|
216 |
(= (clean-aindent--get-line-len) 0)))) |
|
217 |
|
|
218 |
(defun clean-aindent--prev-line() |
|
219 |
"Move cursor to previous line, skip empty lines" |
|
220 |
(let ((c (point))) |
|
221 |
(while |
|
222 |
(and |
|
223 |
(= 0 (forward-line -1)) |
|
224 |
(clean-aindent--line-emptyp))) |
|
225 |
;; return 't if we moved, nil if already beginning of buffer |
|
226 |
(not (= c (point))))) |
|
227 |
|
|
228 |
(defun clean-aindent--find-u-indent(start) |
|
229 |
"Searches lines backward, finds the one that is indented less |
|
230 |
than certain indentation t" |
|
231 |
(save-excursion |
|
232 |
(let (c) |
|
233 |
(while |
|
234 |
(and |
|
235 |
(setq c (current-indentation)) |
|
236 |
(> c 0) |
|
237 |
;; Find an indent smaller than _start_ |
|
238 |
(<= start c) |
|
239 |
;; Walk lines backward |
|
240 |
(clean-aindent--prev-line))) |
|
241 |
;; _c_ is the computed unindent size |
|
242 |
c))) |
|
243 |
|
|
244 |
(defun clean-aindent--inside-indentp() |
|
245 |
"Returns true if cursor is in the leading whitespace or first |
|
246 |
non-blank character of a line" |
|
247 |
(save-excursion |
|
248 |
(let ((pos (point))) |
|
249 |
(beginning-of-line nil) |
|
250 |
(skip-chars-forward " \t") |
|
251 |
(if (<= pos (point)) |
|
252 |
t |
|
253 |
nil)))) |
|
254 |
|
|
255 |
(defun clean-aindent--line-point() |
|
256 |
"Get (point) at the beginning of the current line" |
|
257 |
(save-excursion |
|
258 |
(beginning-of-line) |
|
259 |
(point))) |
|
260 |
|
|
261 |
(defun clean-aindent--goto-column(col) |
|
262 |
"Moves the cursor to a certain column position. |
|
263 |
Column position is different from char position because of TABs" |
|
264 |
(beginning-of-line nil) |
|
265 |
(while (< (current-column) col) |
|
266 |
(right-char))) |
|
267 |
|
|
268 |
(defun clean-aindent--bsunindent(arg) |
|
269 |
"Unindents. |
|
270 |
Bound to `M-backspace' key. Searches lines backward, finds the one that |
|
271 |
is indented less than the current one. Unindents current line to |
|
272 |
align with that smaller indentation" |
|
273 |
(interactive "p") |
|
274 |
(if (not (clean-aindent--inside-indentp)) |
|
275 |
(kill-word (- arg)) ;; Original "C-backspace" key function |
|
276 |
;; else: cursor is inside indent space, do unindent |
|
277 |
(let* |
|
278 |
((ln (clean-aindent--line-point)) |
|
279 |
(c (current-indentation)) |
|
280 |
(n (clean-aindent--find-u-indent c)) ;; compute new indent |
|
281 |
(s (+ ln n))) ;; start of region to delete |
|
282 |
(if (not (= s c)) |
|
283 |
(progn |
|
284 |
;; (message "new unindent %d" n) |
|
285 |
;; Delete characters between s to c |
|
286 |
(clean-aindent--goto-column c) |
|
287 |
(backward-delete-char-untabify (- c n))))))) |
|
288 |
|
|
289 |
|
|
290 |
;; |
|
291 |
;; Initial setup |
|
292 |
;; |
|
293 |
|
|
294 |
(defvar clean-aindent--last-indent nil) |
|
295 |
(defvar clean-aindent--last-indent-len 0) |
|
296 |
|
|
297 |
(defvar clean-aindent-mode--keymap (make-keymap) "clean-aindent-mode keymap.") |
|
298 |
(define-key clean-aindent-mode--keymap [remap backward-kill-word] 'clean-aindent--bsunindent) |
|
299 |
|
|
300 |
;;;###autoload |
|
301 |
(define-minor-mode clean-aindent-mode |
|
302 |
"Activates clean auto indent for function 'newline-and-indent' and |
|
303 |
back-space unindent for M-DEL (meta-backspace). |
|
304 |
|
|
305 |
clean-aindent mode is a global minor mode. |
|
306 |
|
|
307 |
1. Extension of 'newline-and-indent' that keeps track of the last |
|
308 |
auto-indent operation and, if it is abandoned, would take care to |
|
309 |
trim down the unused white space characters. |
|
310 |
|
|
311 |
2. Simple indent, if activated, where cursor is aligned with |
|
312 |
indent of the lines above. |
|
313 |
|
|
314 |
3. Backspace Unindent. Extension of M-backspace. When cursor is |
|
315 |
in the indentation space of a line, or at the first character and |
|
316 |
you press M-backspace it will move the entire line to be aligned |
|
317 |
to the line above or any other that is with indentation smaller |
|
318 |
than the current." |
|
319 |
:init-value nil ; The initial value - disabled by default |
|
320 |
:global t ; Global minor mode |
|
321 |
:keymap clean-aindent-mode--keymap |
|
322 |
;; BODY |
|
323 |
(if clean-aindent-mode |
|
324 |
;; Activate |
|
325 |
(progn |
|
326 |
(ad-enable-advice 'newline-and-indent 'around 'clean-aindent) |
|
327 |
(ad-activate 'newline-and-indent) |
|
328 |
(add-hook 'post-command-hook 'clean-aindent--check-last-point)) |
|
329 |
;; Deactivate |
|
330 |
(progn |
|
331 |
(ad-disable-advice 'newline-and-indent 'around 'clean-aindent) |
|
332 |
(ad-activate 'newline-and-indent) |
|
333 |
(remove-hook 'post-command-hook 'clean-aindent--check-last-point)))) |
|
334 |
|
|
335 |
(provide 'clean-aindent-mode) |
|
336 |
;;; clean-aindent-mode.el ends here |