]> code.delx.au - gnu-emacs-elpa/blob - aggressive-indent.el
Improve css-mode support
[gnu-emacs-elpa] / aggressive-indent.el
1 ;;; aggressive-indent.el --- Minor mode to aggressively keep your code always indented
2
3 ;; Copyright (C) 2014 Artur Malabarba <bruce.connor.am@gmail.com>
4
5 ;; Author: Artur Malabarba <bruce.connor.am@gmail.com>
6 ;; URL: http://github.com/Bruce-Connor/aggressive-indent-mode
7 ;; Version: 0.2
8 ;; Package-Requires: ((emacs "24.1") (names "0.5") (cl-lib "0.5"))
9 ;; Keywords: indent lisp maint tools
10 ;; Prefix: aggressive-indent
11 ;; Separator: -
12
13 ;;; Commentary:
14 ;;
15 ;; `electric-indent-mode' is enough to keep your code nicely aligned when
16 ;; all you do is type. However, once you start shifting blocks around,
17 ;; transposing lines, or slurping and barfing sexps, indentation is bound
18 ;; to go wrong.
19 ;;
20 ;; `aggressive-indent-mode' is a minor mode that keeps your code always
21 ;; indented. It reindents after every command, making it more reliable
22 ;; than `electric-indent-mode'.
23 ;;
24 ;; ### Instructions ###
25 ;;
26 ;; This package is available fom Melpa, you may install it by calling
27 ;;
28 ;; M-x package-install RET aggressive-indent
29 ;;
30 ;; Then activate it with
31 ;;
32 ;; (add-hook 'emacs-lisp-mode-hook #'aggressive-indent-mode)
33 ;; (add-hook 'css-mode-hook #'aggressive-indent-mode)
34 ;;
35 ;; You can use this hook on any mode you want, `aggressive-indent' is not
36 ;; exclusive to emacs-lisp code. In fact, if you want to turn it on for
37 ;; every programming mode, you can do something like:
38 ;;
39 ;; (global-aggressive-indent-mode 1)
40 ;; (add-to-list 'aggressive-indent-excluded-modes 'html-mode)
41 ;;
42 ;; ### Manual Installation ###
43 ;;
44 ;; If you don't want to install from Melpa, you can download it manually,
45 ;; place it in your `load-path' and require it with
46 ;;
47 ;; (require 'aggressive-indent)
48
49 ;;; Instructions:
50 ;;
51 ;; INSTALLATION
52 ;;
53 ;; This package is available fom Melpa, you may install it by calling
54 ;; M-x package-install RET aggressive-indent.
55 ;;
56 ;; Then activate it with
57 ;; (add-hook 'emacs-lisp-mode-hook #'aggressive-indent-mode)
58 ;;
59 ;; You can also use an equivalent hook for another mode,
60 ;; `aggressive-indent' is not exclusive to emacs-lisp code.
61 ;;
62 ;; Alternatively, you can download it manually, place it in your
63 ;; `load-path' and require it with
64 ;;
65 ;; (require 'aggressive-indent)
66
67 ;;; License:
68 ;;
69 ;; This file is NOT part of GNU Emacs.
70 ;;
71 ;; This program is free software; you can redistribute it and/or
72 ;; modify it under the terms of the GNU General Public License
73 ;; as published by the Free Software Foundation; either version 2
74 ;; of the License, or (at your option) any later version.
75 ;;
76 ;; This program is distributed in the hope that it will be useful,
77 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
78 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
79 ;; GNU General Public License for more details.
80 ;;
81
82 ;;; Change Log:
83 ;; 0.2 - 2014/10/20 - Reactivate `electric-indent-mode'.
84 ;; 0.2 - 2014/10/19 - Add variable `aggressive-indent-dont-indent-if', so the user can prevent indentation.
85 ;; 0.1 - 2014/10/15 - Release.
86 ;;; Code:
87
88 (require 'cl-lib)
89 (require 'names)
90
91 ;;;###autoload
92 (define-namespace aggressive-indent- :group indent
93
94 (defconst version "0.2" "Version of the aggressive-indent.el package.")
95 (defun bug-report ()
96 "Opens github issues page in a web browser. Please send any bugs you find.
97 Please include your emacs and aggressive-indent versions."
98 (interactive)
99 (message "Your `aggressive-indent-version' is: %s, and your emacs version is: %s.
100 Please include this in your report!"
101 version emacs-version)
102 (browse-url "https://github.com/Bruce-Connor/aggressive-indent-mode/issues/new"))
103
104 \f
105 ;;; Start of actual Code:
106 (defcustom excluded-modes
107 '(text-mode tabulated-list-mode special-mode
108 minibuffer-inactive-mode
109 yaml-mode jabber-chat-mode)
110 "Modes in which `aggressive-indent-mode' should not be activated.
111 This variable is only used if `global-aggressive-indent-mode' is
112 active. If the minor mode is turned on with the local command,
113 `aggressive-indent-mode', this variable is ignored."
114 :type '(repeat symbol)
115 :package-version '(aggressive-indent . "0.2"))
116
117 (defcustom protected-commands '(undo undo-tree-undo undo-tree-redo)
118 "Commands after which indentation will NOT be performed.
119 Aggressive indentation could break things like `undo' by locking
120 the user in a loop, so this variable is used to control which
121 commands will NOT be followed by a re-indent."
122 :type '(repeat symbol)
123 :package-version '(aggressive-indent . "0.1"))
124
125 (defvar -internal-dont-indent-if
126 '((memq last-command aggressive-indent-protected-commands)
127 (region-active-p)
128 buffer-read-only
129 (null (buffer-modified-p)))
130 "List of forms which prevent indentation when they evaluate to non-nil.
131 This is for internal use only. For user customization, use
132 `aggressive-indent-dont-indent-if' instead.")
133
134 (eval-after-load 'yasnippet
135 '(when (boundp 'yas--active-field-overlay)
136 (add-to-list 'aggressive-indent--internal-dont-indent-if
137 '(and
138 (overlayp yas--active-field-overlay)
139 (overlay-end yas--active-field-overlay))
140 'append)))
141 (eval-after-load 'company
142 '(when (boundp 'company-candidates)
143 (add-to-list 'aggressive-indent--internal-dont-indent-if
144 'company-candidates)))
145 (eval-after-load 'auto-complete
146 '(when (boundp 'ac-completing)
147 (add-to-list 'aggressive-indent--internal-dont-indent-if
148 'ac-completing)))
149
150 (eval-after-load 'css-mode
151 '(add-hook
152 'css-mode-hook
153 (lambda () (unless defun-prompt-regexp
154 (setq-local defun-prompt-regexp "^[^[:blank:]].*")))))
155
156 (defcustom dont-indent-if '()
157 "List of variables and functions to prevent aggressive indenting.
158 This variable is a list where each element is a lisp form.
159 As long as any one of these forms returns non-nil,
160 aggressive-indent will not perform any indentation.
161
162 See `aggressive-indent--internal-dont-indent-if' for usage examples."
163 :type '(repeat sexp)
164 :group 'aggressive-indent
165 :package-version '(aggressive-indent . "0.2"))
166
167 (defvar -error-message
168 "One of the forms in `aggressive-indent-dont-indent-if' had the following error, I've disabled it until you fix it: %S"
169 "Error message thrown by `aggressive-indent-dont-indent-if'.")
170
171 (defvar -has-errored nil
172 "Keep track of whether `aggressive-indent-dont-indent-if' is throwing.
173 This is used to prevent an infinite error loop on the user.")
174
175 (defun -softly-indent-defun ()
176 "Indent current defun unobstrusively.
177 Like `aggressive-indent-indent-defun', except do nothing if
178 mark is active (to avoid deactivaing it), or if buffer is not
179 modified (to avoid creating accidental modifications).
180 Also, never throw errors nor messages.
181
182 Meant for use in hooks. Interactively, use the other one.
183 Indentation is not performed if any of the forms in
184 `dont-indent-if' evaluates to non-nil."
185 (unless (or (run-hook-wrapped
186 'aggressive-indent--internal-dont-indent-if
187 #'eval)
188 (-run-user-hooks))
189 (ignore-errors
190 (cl-letf (((symbol-function 'message) #'ignore))
191 (indent-defun)))))
192
193 (defun -run-user-hooks ()
194 "Safely run forms in `aggressive-indent-dont-indent-if'.
195 If any of them errors out, we only report it once until it stops
196 erroring again."
197 (and dont-indent-if
198 (condition-case er
199 (prog1 (eval (cons 'or dont-indent-if))
200 (setq -has-errored nil))
201 (error
202 (unless -has-errored
203 (setq -has-errored t)
204 (message -error-message er))))))
205
206 :autoload
207 (defun indent-defun ()
208 "Indent current defun.
209 Throw an error if parentheses are unbalanced."
210 (interactive)
211 (let ((p (point-marker)))
212 (set-marker-insertion-type p t)
213 (indent-region
214 (save-excursion (beginning-of-defun 1) (point))
215 (save-excursion (end-of-defun 1) (point)))
216 (goto-char p)))
217
218 \f
219 ;;; Minor modes
220 :autoload
221 (define-minor-mode mode nil nil " =>"
222 '(("\C-c\C-q" . aggressive-indent-indent-defun))
223 (if mode
224 (if (and global-aggressive-indent-mode
225 (or (cl-member-if #'derived-mode-p excluded-modes)
226 buffer-read-only))
227 (mode -1)
228 (when (fboundp 'electric-indent-local-mode)
229 (electric-indent-local-mode 1))
230 (add-hook 'post-command-hook #'-softly-indent-defun nil 'local))
231 (remove-hook 'post-command-hook #'-softly-indent-defun 'local)))
232
233 :autoload
234 (define-globalized-minor-mode global-aggressive-indent-mode
235 mode mode)
236
237 :autoload
238 (defalias 'aggressive-indent-global-mode
239 #'global-aggressive-indent-mode)
240 )
241
242 (provide 'aggressive-indent)
243 ;;; aggressive-indent.el ends here.