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