mirror of https://github.com/Chizi123/.emacs.d.git

Chizi123
2018-11-18 9d27fc972e84736015ab3b1c331888a8fe3d1276
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