]> git.siccegge.de Git - dotfiles/emacs.git/blob - lisp/smart-tabs-mode.el
Import elisp libraries
[dotfiles/emacs.git] / lisp / smart-tabs-mode.el
1 ;;; smart-tabs-mode.el --- Intelligently indent with tabs, align with spaces!
2
3 ;; Copyright © 2011 John Croisant <jacius@gmail.com>
4 ;; Copyright © 2011 Joel C. Salomon <joelcsalomon@gmail.com>
5 ;; Copyright © 2012 Alan Pearce <alan@alanpearce.co.uk>
6 ;; Copyright © 2012 Daniel Dehennin <daniel.dehennin@baby-gnu.org>
7 ;; Copyright © 2013 Matt Renaud <mrenaud92@gmail.com>
8
9 ;; Author: John Croisant <jacius@gmail.com>
10 ;; Alan Pearce <alan@alanpearce.co.uk>
11 ;; Daniel Dehennin <daniel.dehennin@baby-gnu.org>
12 ;; Matt Renaud <mrenaud92@gmail.com>
13 ;; Maintainer: Joel C. Salomon <joelcsalomon@gmail.com>
14 ;; URL: http://www.emacswiki.org/emacs/SmartTabs
15 ;; Created: 19 Sep 2011
16 ;; Version: 1.0
17 ;; Keywords: languages
18
19 ;; This file is not part of GNU Emacs.
20
21 ;;; License:
22
23 ;; This file is free software: you can redistribute it and/or modify
24 ;; it under the terms of the GNU General Public License as published by
25 ;; the Free Software Foundation, either version 2 of the License, or
26 ;; (at your option) any later version.
27 ;;
28 ;; This file is distributed in the hope that it will be useful,
29 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
30 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 ;; GNU General Public License for more details.
32 ;;
33 ;; You should have received a copy of the GNU General Public License
34 ;; along with this file. If not, see <http://www.gnu.org/licenses/>.
35
36 ;;; Commentary:
37
38 ;; This package provide a semantic way of using tab characters in
39 ;; source code: tabs for indentation, spaces for alignment.
40 ;;
41 ;; It is derived from <http://www.emacswiki.org/emacs/SmartTabs>
42 ;; with modifications by the various authors listed above.
43 ;;
44 ;; Modification history is at <https://github.com/jcsalomon/smarttabs>.
45
46 ;;; Installation:
47
48 ;; The easiest and preferred way to install smart-tabs-mode is to use
49 ;; the package available on MELPA.
50 ;;
51 ;; Manual installation:
52 ;;
53 ;; Save smart-tabs-mode.el to a a directory on your load-path
54 ;; (e.g., ~/.emacs.d/elisp), then add the following to your .emacs file:
55 ;;
56 ;; (autoload 'smart-tabs-mode "smart-tabs-mode"
57 ;; "Intelligently indent with tabs, align with spaces!")
58 ;; (autoload 'smart-tabs-mode-enable "smart-tabs-mode")
59 ;; (autoload 'smart-tabs-advice "smart-tabs-mode")
60 ;; (autoload 'smart-tabs-insinuate "smart-tabs-mode")
61 ;;
62
63 ;;; Enabling smart-tabs-mode within language modes:
64
65 ;; As of version 1.0 of this package, the easiest and preferred way to
66 ;; enable smart-tabs-mode is with the smart-tabs-insinuate function;
67 ;; for example,
68 ;;
69 ;; (smart-tabs-insinuate 'c 'c++ 'java 'javascript 'cperl 'python
70 ;; 'ruby 'nxml)
71 ;;
72 ;; will enable smart-tabs-mode with all supported language modes.
73 ;;
74 ;; (See below for instructions on adding additional language support.)
75 ;;
76 ;; The old method of manually enabling smart-tabs-mode is still
77 ;; available, but is no longer recommended; smart-tabs-insinuate
78 ;; wraps the functionality below in a convenient manner.
79 ;;
80 ;; For reference, the basic manual method looks like this:
81 ;;
82 ;; (add-hook 'MODE-HOOK 'smart-tabs-mode-enable)
83 ;; (smart-tabs-advice INDENT-FUNC TAB-WIDTH-VAR)
84 ;;
85 ;; Note that it might be preferable to delay calling smart-tabs-advice
86 ;; until after the major mode is loaded and evaluated, so the lines
87 ;; above would be better written like this:
88 ;;
89 ;; (add-hook 'MODE-HOOK (lambda ()
90 ;; (smart-tabs-mode-enable)
91 ;; (smart-tabs-advice INDENT-FUNC TAB-WIDTH-VAR)))
92 ;;
93 ;; Here are some specific examples for a few popular languages:
94 ;;
95 ;; ;; C
96 ;; (add-hook 'c-mode-hook 'smart-tabs-mode-enable)
97 ;; (smart-tabs-advice c-indent-line c-basic-offset)
98 ;; (smart-tabs-advice c-indent-region c-basic-offset)
99 ;;
100 ;; ;; JavaScript
101 ;; (add-hook 'js2-mode-hook 'smart-tabs-mode-enable)
102 ;; (smart-tabs-advice js2-indent-line js2-basic-offset)
103 ;;
104 ;; ;; Perl (cperl-mode)
105 ;; (add-hook 'cperl-mode-hook 'smart-tabs-mode-enable)
106 ;; (smart-tabs-advice cperl-indent-line cperl-indent-level)
107 ;;
108 ;; ;; Python
109 ;; (add-hook 'python-mode-hook 'smart-tabs-mode-enable)
110 ;; (smart-tabs-advice python-indent-line-1 python-indent)
111
112 ;;; Adding language support
113
114 ;; Language support can be added through the use of the macro
115 ;; `smart-tabs-add-language-support'. Pass in the symbol you wish
116 ;; to use to identify the language, the mode hook, and a list
117 ;; of cons cells containing (indent-line-function . offset-variable).
118 ;; For example, if C++ mode was not provided by default it could be
119 ;; added as follows:
120 ;;
121 ;; (smart-tabs-add-language-support c++ c++-mode-hook
122 ;; ((c-indent-line . c-basic-offset)
123 ;; (c-indent-region . c-basic-offset)))
124 ;;
125 ;; NOTE: All language support must be added before the call to
126 ;; `smart-tabs-insinuate'.
127
128 ;;; Code:
129 \f
130 (require 'advice)
131 (require 'cl-lib)
132
133 (defvar smart-tabs-mode nil
134 "Define if smart-tabs-mode is enabled")
135
136
137 ;;;###autoload
138 (defmacro smart-tabs-when (condition advice-list)
139 (declare (indent 1))
140 `(when ,condition
141 ,@(smart-tabs-create-advice-list advice-list)))
142
143
144 ;;;###autoload
145 (defmacro smart-tabs-create-advice-list (advice-list)
146 `(cl-loop for (func . offset) in ,advice-list
147 collect `(smart-tabs-advice ,func ,offset)))
148
149
150 ;;;###autoload
151 (defmacro smart-tabs-create-language-advice (lang mode-hook advice-list &rest body)
152 "Create a cons cell containing the actions to take to enable
153 `smart-tabs-mode' for the language LANG. This usually involved enabling
154 `smart-tabs-mode' through `smart-tabs-mode-enable' and adding a lambda
155 function to the MODE-HOOK for the specified language. This macro
156 simplifies the creation of such a cons cell."
157 (declare (indent 2))
158 `'(,lang . (lambda ()
159 (add-hook ',mode-hook
160 (lambda ()
161 (smart-tabs-mode-enable)
162 ,@(smart-tabs-create-advice-list advice-list)
163 ,@(cl-loop for form in body
164 collect (macroexpand form)))))))
165
166
167 (defvar smart-tabs-insinuate-alist
168 `(,(smart-tabs-create-language-advice c c-mode-hook
169 ((c-indent-line . c-basic-offset)
170 (c-indent-region . c-basic-offset)))
171
172 ,(smart-tabs-create-language-advice c++ c++-mode-hook
173 ((c-indent-line . c-basic-offset)
174 (c-indent-region . c-basic-offset)))
175
176 ,(smart-tabs-create-language-advice java java-mode-hook
177 ((c-indent-line . c-basic-offset)
178 (c-indent-region . c-basic-offset)))
179
180 ,(smart-tabs-create-language-advice javascript js2-mode-hook
181 ((js2-indent-line . js2-basic-offset)))
182
183 ,(smart-tabs-create-language-advice cperl cperl-mode-hook
184 ((cperl-indent-line . cperl-indent-level)))
185
186 ,(smart-tabs-create-language-advice python python-mode-hook
187 ((python-indent-line . python-indent-offset)
188 (python-indent-region . python-indent-offset))
189 (smart-tabs-when (featurep 'python-mode)
190 ((py-indent-line . py-indent-offset)
191 (py-newline-and-indent . py-indent-offset)
192 (py-indent-region . py-indent-offset))))
193
194 ,(smart-tabs-create-language-advice ruby ruby-mode-hook
195 ((ruby-indent-line . ruby-indent-level)))
196
197 ,(smart-tabs-create-language-advice nxml nxml-mode-hook
198 ((nxml-indent-line . nxml-child-indent))))
199
200 "Alist of language name and their activation code.
201 Smarttabs is enabled in mode hook.")
202
203
204
205 (defmacro smart-tabs-mode/no-tabs-mode-advice (function)
206 `(unless (ad-find-advice ',function 'around 'smart-tabs)
207 (defadvice ,function (around smart-tabs activate)
208 (if smart-tabs-mode
209 (let ((indent-tabs-mode nil)) ad-do-it)
210 ad-do-it))))
211
212 ;;;###autoload
213 (define-minor-mode smart-tabs-mode
214 "Intelligently indent with tabs, align with spaces!"
215
216 (progn
217 (smart-tabs-mode/no-tabs-mode-advice align)
218 (smart-tabs-mode/no-tabs-mode-advice align-regexp)
219 (smart-tabs-mode/no-tabs-mode-advice indent-relative)
220 (smart-tabs-mode/no-tabs-mode-advice comment-dwim)
221 (smart-tabs-mode/no-tabs-mode-advice comment-box)
222 (smart-tabs-mode/no-tabs-mode-advice comment-indent)
223
224 (unless
225 (ad-find-advice 'indent-according-to-mode 'around 'smart-tabs)
226 (defadvice indent-according-to-mode (around smart-tabs activate)
227 (if smart-tabs-mode
228 (let ((indent-tabs-mode indent-tabs-mode))
229 (if (memq indent-line-function
230 '(indent-relative
231 indent-relative-maybe))
232 (setq indent-tabs-mode nil))
233 ad-do-it)
234 ad-do-it)))
235 ))
236
237 ;;;###autoload
238 (defun smart-tabs-mode-enable ()
239 "Enable smart-tabs-mode."
240 (smart-tabs-mode t))
241
242 ;;;###autoload
243 (defmacro smart-tabs-advice (function offset)
244 `(progn
245 (defadvice ,function (around smart-tabs activate)
246 (cond
247 (smart-tabs-mode
248 (save-excursion
249 (beginning-of-line)
250 (while (looking-at "\t*\\( +\\)\t+")
251 (replace-match "" nil nil nil 1)))
252 (setq tab-width tab-width)
253 (let ((indent-tabs-mode t)
254 (tab-width fill-column)
255 (,offset fill-column))
256 (unwind-protect
257 (progn ad-do-it))))
258 (t
259 ad-do-it)))))
260
261 ;;;###autoload
262 (defun smart-tabs-insinuate (&rest languages)
263 "Enable smart-tabs-mode for LANGUAGES.
264 LANGUAGES is a list of SYMBOL or NAME as defined in
265 'smart-tabs-insinuate-alist' alist or a language using standard named
266 indent function and indent level.
267 "
268 (mapc (lambda (lang)
269 (let ((lang-map (assoc lang smart-tabs-insinuate-alist))
270 (lang-param (smart-tabs-get-standard-language lang)))
271 (cond ((and (null lang-map)
272 (not (null (car lang-param)))
273 (not (null (nth 1 lang-param)))
274 (not (null (nth 2 lang-param))))
275 (smart-tabs-guess-insinuate lang-param))
276 ((null lang-map)
277 (error (format "Unknown smart-tab-mode capable language '%s'" lang)))
278 (t (funcall (cdr lang-map))))))
279 languages))
280
281
282 ;;;###autoload
283 (defmacro smart-tabs-add-language-support (lang mode-hook advice-list &rest body)
284 "Add support for a language not already in the `smart-tabs-insinuate-alist'."
285 (declare (indent 2))
286 `(add-to-list
287 'smart-tabs-insinuate-alist
288 (smart-tabs-create-language-advice ,lang ,mode-hook
289 ,advice-list ,@body)))
290
291
292 (defun smart-tabs-guess-insinuate (lang-param)
293 "Enable smart-tabs-mode if language respect standard naming.
294 Several languages define a '<LANGUAGE>-indent-line' function and
295 '<LANGUAGE>-indent-level' variable to control indentation.
296 LANG-PARAM is a list of HOOK INDENT-FUNCTION INDENT-LEVEL, if
297 thoses are defined, we use them."
298 (let ((lang-hook (car lang-param))
299 (indent-function (nth 1 lang-param))
300 (indent-level (nth 2 lang-param)))
301 (if (and (not (null lang-hook))
302 (and (not (null indent-function))
303 (fboundp indent-function))
304 (and (not (null indent-level))
305 (boundp indent-level)))
306 (add-hook lang-hook
307 `(lambda ()
308 (smart-tabs-mode-enable)
309 (smart-tabs-advice ,indent-function ,indent-level))))))
310
311 (defun smart-tabs-get-standard-language (language)
312 "Return a list of HOOK INDENT-FUNCTION INDENT-LEVEL for a language."
313 (let ((indent-function (intern-soft (concat (symbol-name language) "-indent-line")))
314 (indent-level (intern-soft (concat (symbol-name language) "-indent-level")))
315 (lang-hook (intern-soft (concat (symbol-name language) "-mode-hook"))))
316 (list lang-hook indent-function indent-level)))
317
318 (provide 'smart-tabs-mode)
319
320 ;;; smart-tabs-mode.el ends here