1 ;;; smart-tabs-mode.el --- Intelligently indent with tabs, align with spaces!
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>
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
17 ;; Keywords: languages
19 ;; This file is not part of GNU Emacs.
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.
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.
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/>.
38 ;; This package provide a semantic way of using tab characters in
39 ;; source code: tabs for indentation, spaces for alignment.
41 ;; It is derived from <http://www.emacswiki.org/emacs/SmartTabs>
42 ;; with modifications by the various authors listed above.
44 ;; Modification history is at <https://github.com/jcsalomon/smarttabs>.
48 ;; The easiest and preferred way to install smart-tabs-mode is to use
49 ;; the package available on MELPA.
51 ;; Manual installation:
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:
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")
63 ;;; Enabling smart-tabs-mode within language modes:
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;
69 ;; (smart-tabs-insinuate 'c 'c++ 'java 'javascript 'cperl 'python
72 ;; will enable smart-tabs-mode with all supported language modes.
74 ;; (See below for instructions on adding additional language support.)
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.
80 ;; For reference, the basic manual method looks like this:
82 ;; (add-hook 'MODE-HOOK 'smart-tabs-mode-enable)
83 ;; (smart-tabs-advice INDENT-FUNC TAB-WIDTH-VAR)
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:
89 ;; (add-hook 'MODE-HOOK (lambda ()
90 ;; (smart-tabs-mode-enable)
91 ;; (smart-tabs-advice INDENT-FUNC TAB-WIDTH-VAR)))
93 ;; Here are some specific examples for a few popular languages:
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)
101 ;; (add-hook 'js2-mode-hook 'smart-tabs-mode-enable)
102 ;; (smart-tabs-advice js2-indent-line js2-basic-offset)
104 ;; ;; Perl (cperl-mode)
105 ;; (add-hook 'cperl-mode-hook 'smart-tabs-mode-enable)
106 ;; (smart-tabs-advice cperl-indent-line cperl-indent-level)
109 ;; (add-hook 'python-mode-hook 'smart-tabs-mode-enable)
110 ;; (smart-tabs-advice python-indent-line-1 python-indent)
112 ;;; Adding language support
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
121 ;; (smart-tabs-add-language-support c++ c++-mode-hook
122 ;; ((c-indent-line . c-basic-offset)
123 ;; (c-indent-region . c-basic-offset)))
125 ;; NOTE: All language support must be added before the call to
126 ;; `smart-tabs-insinuate'.
133 (defvar smart-tabs-mode nil
134 "Define if smart-tabs-mode is enabled")
138 (defmacro smart-tabs-when (condition advice-list)
141 ,@(smart-tabs-create-advice-list advice-list)))
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)))
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."
158 `'(,lang . (lambda ()
159 (add-hook ',mode-hook
161 (smart-tabs-mode-enable)
162 ,@(smart-tabs-create-advice-list advice-list)
163 ,@(cl-loop for form in body
164 collect (macroexpand form)))))))
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)))
172 ,(smart-tabs-create-language-advice c++ c++-mode-hook
173 ((c-indent-line . c-basic-offset)
174 (c-indent-region . c-basic-offset)))
176 ,(smart-tabs-create-language-advice java java-mode-hook
177 ((c-indent-line . c-basic-offset)
178 (c-indent-region . c-basic-offset)))
180 ,(smart-tabs-create-language-advice javascript js2-mode-hook
181 ((js2-indent-line . js2-basic-offset)))
183 ,(smart-tabs-create-language-advice cperl cperl-mode-hook
184 ((cperl-indent-line . cperl-indent-level)))
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))))
194 ,(smart-tabs-create-language-advice ruby ruby-mode-hook
195 ((ruby-indent-line . ruby-indent-level)))
197 ,(smart-tabs-create-language-advice nxml nxml-mode-hook
198 ((nxml-indent-line . nxml-child-indent))))
200 "Alist of language name and their activation code.
201 Smarttabs is enabled in mode hook.")
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)
209 (let ((indent-tabs-mode nil)) ad-do-it)
213 (define-minor-mode smart-tabs-mode
214 "Intelligently indent with tabs, align with spaces!"
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)
225 (ad-find-advice 'indent-according-to-mode 'around 'smart-tabs)
226 (defadvice indent-according-to-mode (around smart-tabs activate)
228 (let ((indent-tabs-mode indent-tabs-mode))
229 (if (memq indent-line-function
231 indent-relative-maybe))
232 (setq indent-tabs-mode nil))
238 (defun smart-tabs-mode-enable ()
239 "Enable smart-tabs-mode."
243 (defmacro smart-tabs-advice (function offset)
245 (defadvice ,function (around smart-tabs activate)
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))
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.
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))
277 (error (format "Unknown smart-tab-mode capable language '%s'" lang)))
278 (t (funcall (cdr lang-map))))))
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'."
287 'smart-tabs-insinuate-alist
288 (smart-tabs-create-language-advice ,lang ,mode-hook
289 ,advice-list ,@body)))
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)))
308 (smart-tabs-mode-enable)
309 (smart-tabs-advice ,indent-function ,indent-level))))))
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)))
318 (provide 'smart-tabs-mode)
320 ;;; smart-tabs-mode.el ends here