3

To reproduce my issue, open your .bashrc or a .sh file and type the following:

if
    while
        echo

The if has no indentation. Good.

The while has the default four spaces of indentation. Good.

The echo has a tab character for indentation. Why?

Are there any hidden values I can set to prevent what should be eight spaces from becoming a tab? I find nothing in M-x customize.


Messing with the following does not help:

'(sh-basic-offset 4)
'(sh-indentation 4)

When I use M-x smie-config-show-indent, the result is this:

Rules used: :elem basic -> 4, :after "while" -> nil

So it doesn't seem like SMIE is responsible. I think shell-script-mode is automatically converting eight spaces to tabs. This isn't happening in any other mode.

This occurs with or without electric-indent-mode.

Ness
  • 165
  • 7

2 Answers2

5

Shell script mode does not have any setting that relates to using tabs vs spaces (at least as of Emacs 25.2). It obeys the default Emacs setting which is given by indent-tabs-mode and tab-width: 8 spaces of indentation are replaced by a tab character.

To turn this off globally, put this line in your init file:

(setq indent-tabs-mode nil)

This doesn't affect modes that have their own settings. For example Python mode turns off the use of tabs, and Makefile mode turns it on, regardless of the global settings.

To turn off the use of tabs in shell script mode only, put this line in your init file:

(defun turn-off-indent-tabs-mode ()
  (setq indent-tabs-mode nil))
(add-hook 'sh-mode-hook #'turn-off-indent-tabs-mode)

If you occasionally work with files that must have tabs or that must not have tabs, you can (and should) set indent-tabs-mode on a per-file or per-directory basis through file-local variables or directory-local variables. In a file, put something like this near the end of the file; the leading string @@@ can be anything as long as it's the same on all these lines.

@@@ Local Variables:
@@@ indent-tabs-mode: t
@@@ tab-width: 8
@@@ End:

In a directory, create a file called .dir-locals.el with content like this:

((nil . ((indent-tabs-mode . nil))
 (javascript-mode . ((indent-tabs-mode t)
                     (tab-width 8))
 ("linux-driver" . ((indent-tabs-mode t)
                    (tab-width 8)))))

This artificial example specifies not to use tabs, except for files in the linux-driver subdirectory and files in JavaScript mode which do use tabs.

Instead of (or in addition to) the Emacs-specific file, you can put an .editorconfig file in your project.

[*]
indent_style = space
indent_size = 4

[{*.js,linux-driver/*}]
indent_style = tab
indent_size = 8

To make Emacs support .editorconfig, install the editorconfig package.

  • Just using the setq doesn't seem to work, but your add-hook does. So perhaps shell-script-mode does indeed have a setting for tab usage. I was reluctant to set indent-tabs-mode globally because of languages like Python, but as you said, python-mode apparently overrides it. Good to know! I also deal with some projects whose owners use tabs by default, so it would not be an ideal global setting for me anyway. I should learn more about these hooks. – Ness Aug 07 '19 at 21:11
  • 1
    @Ness You should add a `.editorconfig` or a `.dir-locals.el` to these projects. – Gilles 'SO- stop being evil' Aug 07 '19 at 22:21
2

Gilles' answer is mostly correct, but indent-tabs-mode is a local variable, so it works per-buffer. You can't use setq to disable the tabs globally. You must use setq-default instead:

(setq-default indent-tabs-mode nil)

The same is about tab-width. If you prefer to have tab of width 2, use the following:

(setq-default tab-width 2)
freehck
  • 21
  • 1