This is about indenting with tabs and aligning with spaces. For Sébastien Rocca-Serra’s smart-tab command, see TabCompletion.

There is a semantic way of using tab characters in source code: tabs for indentation, spaces for alignment. This ensures that the code is displayed correctly everywhere, regardless of the viewer’s tab size.
// Tab size: 4 // Tab size: 2
if(foo) { if(foo) {
--->char quux[4] = {0, 1, ->char quux[4] = {0, 1, // "0" and "2"
--->................2, 3}; ->................2, 3}; // remain aligned
} }The approach can be summed up in two points:
This page provides code which improves on Emacs’ auto-indenting, so that whenever you press the <tab> key, the appropriate amount of tabs and spaces is inserted automatically. You can still use all spaces if you prefer, of course; the code merely ensures that if you enable tabs (maybe when maintaining legacy code), the tab size does not matter.
Add the code below to .emacs to make Emacs insert the proper amount of tabs and spaces automatically, provided indent-tabs-mode is not disabled (see toggling). Then, to indent and align, just press ‘<tab>’ as usual.
You can retab the whole file by pressing ‘C-x h C-M-\’. (TabCompletion provides a way to do this with fewer keystrokes.)
For CC Mode (C, C++, Java et al.), add
(setq-default tab-width 4) ; or any other preferred value
(setq cua-auto-tabify-rectangles nil) (defadvice align (around smart-tabs activate)
(let ((indent-tabs-mode nil)) ad-do-it)) (defadvice align-regexp (around smart-tabs activate)
(let ((indent-tabs-mode nil)) ad-do-it)) (defadvice indent-relative (around smart-tabs activate)
(let ((indent-tabs-mode nil)) ad-do-it)) (defadvice indent-according-to-mode (around smart-tabs activate)
(let ((indent-tabs-mode indent-tabs-mode))
(if (memq indent-line-function
'(indent-relative
indent-relative-maybe))
(setq indent-tabs-mode nil))
ad-do-it)) (defmacro smart-tabs-advice (function offset)
`(progn
(defvaralias ',offset 'tab-width)
(defadvice ,function (around smart-tabs activate)
(cond
(indent-tabs-mode
(save-excursion
(beginning-of-line)
(while (looking-at "\t*\\( +\\)\t+")
(replace-match "" nil nil nil 1)))
(setq tab-width tab-width)
(let ((tab-width fill-column)
(,offset fill-column)
(wstart (window-start)))
(unwind-protect
(progn ad-do-it)
(set-window-start (selected-window) wstart))))
(t
ad-do-it))))) (smart-tabs-advice c-indent-line c-basic-offset)
(smart-tabs-advice c-indent-region c-basic-offset)The rest of the page assumes the above is already in place.
For Js2Mode, add
(smart-tabs-advice js2-indent-line js2-basic-offset)
For CPerlMode, add
(smart-tabs-advice cperl-indent-line cperl-indent-level)
For python.el, add
(smart-tabs-advice python-indent-line-1 python-indent)
(add-hook 'python-mode-hook
(lambda ()
(setq indent-tabs-mode t)
(setq tab-width (default-value 'tab-width))))For python-mode.el, add
(smart-tabs-advice py-indent-line py-indent-offset)
(smart-tabs-advice py-newline-and-indent py-indent-offset)
(smart-tabs-advice py-indent-region py-indent-offset)Smart tabs are, of course, fully Python compliant, as the interpreter ignores the leading whitespace of continuation lines. However, in accordance with PEP 8, you should probably use tabs only when maintaining old code.
For RubyMode, add
(smart-tabs-advice ruby-indent-line ruby-indent-level)
(setq ruby-indent-tabs-mode t)For VhdlMode, add
(smart-tabs-advice vhdl-indent-line vhdl-basic-offset)
(setq vhdl-indent-tabs-mode t)Smart tabs are only used when indent-tabs-mode is non-nil (default). Type ‘C-h v indent-tabs-mode RET’ to see the current, buffer-local value. To change indent-tabs-mode for the current buffer only, type ‘M-x set-variable indent-tabs-mode RET’ or customize the mode hook.
What you probably want to do: Disable tabs globally (spaces only) and reactivate them for modes with smart tabs handling. For CC Mode:
(setq-default indent-tabs-mode nil)
(add-hook 'c-mode-common-hook
(lambda () (setq indent-tabs-mode t)))See GuessStyle for a package which attempts to determine the proper values for indent-tabs-mode and the indentation offset by analysing how the file is formatted. (Of course, since smart tabs are tab size-independent, such files do not have an offset.)
This code is now available as an Emacs minor mode on GitHub; see the file smart-tabs-mode.el. Bug reports, code contributions, etc., are all welcome. —JCSalomon
For those looking to implement this functionality in other modes (or even other editors), this section explains how the code above works. The method used is not specific to Emacs – any scriptable editor with syntactic auto-indentation can be made to indent with tabs and align with spaces.
The editor’s auto-indentation depends on two variables: the tab size and the indentation offset. The tab size is how many columns a tab occupies (more precisely, the distance between tab stops). The indentation offset is the standard amount of columns an indented line is shifted to the right (disregarding special cases like alignment of multi-line expressions).
If we have access to these two variables, we can separate “indentation” from “alignment” without knowing very much about the language itself. The idea is simple: temporarily set the tab size and indentation offset to a very large value before indenting. This is similar to calling GNU Indent with -i99 -ts99 -l999 (along with necessary style parameters).
For instance, say we want to indent the C function
int f(int x,
int y) {
return g(x,
y);
}We drastically “expand” the tab size and indentation offset (they must have the same value, so that one tab equals one indentation offset), and perform the indenting:
int f(int x,
......int y) {
----------------------->return g(x,
----------------------->.........y);
}When we change the values back – say to 4 – we have
int f(int x,
......int y) {
--->return g(x,
--->.........y);
}which is indeed tab size-independent.
This works because (1) setting the tab size and indentation offset to the same value ensures that all “instances” of the offset are encoded as tabs, (2) using a value that is larger than any amount of alignment forces spaces to be used for the rest. (A good value is probably a multiple of the maximum line length.)
This situation could arise if the user accidently hits ‘SPC’ at the beginning of a line. Emacs’ indentation functions may not be able to detect and remove such spaces, because the perceived amount of indentation is unchanged.
if(cond) {
.-->return 0;To be on the safe side, we see to that such spaces are weeded out before performing “expansion” and indentation:
if(cond) {
--->return 0;In Emacs, the tab size is controlled by the buffer-local variable tab-width, while the indentation offset differs from mode to mode. The macro smart-tabs-advice constructs a wrap-around advice of the mode’s indentation function, which shadows these variables. That is, the original values of tab-width and the indentation offset are temporarily overwritten with a large value (fill-column). The advice also checks the beginning of the line for spaces preceding tabs; if any are found, they are deleted before indenting.
So, to add smart tabs support for a new language, all one has to do is investigate its major mode, identify the indentation functions and the offset variable, and pass them to smart-tabs-advice as such:
(smart-tabs-advice language-indent-line language-basic-offset)
Also, ensure that the mode has tabs activated. Some modes, such as VhdlMode, control this with their own variable rather than through Emacs’ standard indent-tabs-mode.
The wheel appears to have been reinvented a number of times:
(As for non-Emacs implementations, Michael Geddes has written a Smart Tabs plugin for Vim. Also, IntelliJ IDEA has a “Smart Tabs” option, Anjuta’s tab handling is reportedly fixed in SVN, and the Kate editor’s indentation API distinguishes between “indentation” and “alignment”.)
I tried to use smart-tabs-mode.el working with sh-mode and did not managed to, any idea? --DanielDehennin