- Edit a 2-space indented file as if it was using a wider indentation!
- Seamlessly convert space-based indented files to tab-based indentation for viewing and editing.
- Decouple the file indentation-scheme from what buffer uses for viewing and editing.
This package provides tbindent-mode, a minor mode which provides the ability to transparently convert the content of a file indented with a fixed number of spaces into a buffer which uses tab-based indentation and save the buffer content back to file with the original space-based indentation scheme.
Example 1
Manually turning tbindent-mode on and off to edit a 2-space indented Gleam
file as if it used 4-space indentation.
Example 2
Edit a 2-space indented Dart file in a 4 columns indented buffer, with
the tbindent-mode hooked for the dart-ts-mode` major mode and the
dtrt-indent-mode used:
- Open the tweens.dart file in the top window buffer.
- The
*Message*buffer in the bottom window showsdtrt-indent-modedetecting the indentation of the file andtbindent-modetaking over.
- The
- Open a terminal shell buffer in the bottom window.
- inside the shell execute
viand open the same file inside Vim (which runs in a terminal running inside Emacs).
- inside the shell execute
- Go back to the top window, duplicate one line. The buffer shows the line as if it was indented by 4 columns. Save the file.
- Move to the bottom window and refresh Vim to see the changes. The new line is shown indented with 2 columns as the line above.
- Move to the top window and invoke the file diff which opens in the bottom window and shows that the new created line is indeed indented with 2 spaces.
Why would you want to use this?
- You have problems or don't like working with files using narrow indentation, you can't change the file indentation but want a tool that can make it look wider.
- Edit space-based indentation files as if they were tab-based.
- Use it to edit a file using a wider or narrower indentation width.
You can use this minor mode if you'd like to see the indentation with a different width while viewing or editing its content. That may appeal to people that have problems or do not like working with code that use a small space-based indentation width. With the rising popularity of space based indentation with a 2 column indentation width, the need for a work-around solution increases.
How does it work?
Given appropriate conditions, the tbindent-mode converts a buffer content from space-based indentation to pure hard-tabs based indentation.
The conditions are:
- the code must be able to identify the name of variables used to control
indentation for the major mode from hard-coded associated list
tbindent--mode-indent-varsor from the user-specifiedtbindent-extra-mode-indent-vars.- It's strongly recommended to install the dtrt-indent package, allowing
tbindent-modeto activate``dtrt-indent-mode`` before checking the indentation control variable, minimizing changes of misinterpretation of the file's indentation scheme.
- It's strongly recommended to install the dtrt-indent package, allowing
- the value of
tab-widthmust be equal to the indentation value identified by the indentation control variable used for the major-mode, such as, for example:c-basic-offsetin C, C++ , D buffers, orada-indentfor Ada buffers, orpython-indent-offsetfor Python buffers.
- Those values must also represent the real indentation width used inside the file.
When turning on:
tbindent-modechecks if the indentation variable for the major mode is known and issue an error with instructions if it is unknown.tbindent-modecompares the value oftab-widthwith the value of the indentation control variable. If they differ, and if dtrt-indent is available, it sets the local value oftab-widthto the value of the indentation control variable and issues a message. If dtrt-indent is not availabletbindent-modeissues an error with instructions.
If tbindent-mode activates, the indentation step used in the buffer
identified by the indentation control variable corresponds to the tab width.
The code can now change the tab-width and the value of the indentation control
variable to change the visual rendering of indentation to be narrower or
wider.
The tbindent code includes a look-up table mapping major mode to the indentation control variable(s) that mode uses. You can add more mappings or even write mappings that override the built-in ones.
Activation/deactivation:
- When turning tbindent-mode on:
- it remembers the original tab-width and indentation scheme,
- converts each group of spaces that constitute one indentation step into
one hard tab and adjust the value of the following buffer local variables:
- it sets both
tab-widthand the variable(s) that controls indentation for the buffer's major-mode to the same value, using the variables tbindent-target-indent-widths customizable user-option defines if any or the one(s) currently used, - it activates the
indent-tabs-mode, - it manages the
fill-columnvalue (more on this later).
- it sets both
- When turning tbindent-mode off:
- it restores the original tab-width
- it converts the content of the buffer back to the original space-based indentation scheme.
Saving To File
When tbindent-mode is active and you save the buffer back to its file, the mode seamlessly converts the buffer content back to its original space-based indentation scheme, stores the buffer content into the file and then restore the tabs-based indentation to let you continue editing the file.
Undoing
The tbindent mode excludes all conversions from space-based indentation to tabs-based from the buffer's undo list. They do not show up as a buffer modification. If you think about it, that's really the case anyways; those conversions are just a visual rendering change, the file is not modified.
Toggle the tbindent-mode
To activate or de-activate the mode, type:
M-x tbindent-mode
Dynamically changing the indentation width
While tbindent-mode is on, use the tbindent-set-tab-width command to change the indentation width.
M-x tbindent-set-tab-width
That command changes the buffer local value of
tab-width and the indentation control variable used by the major mode.
With that command, you can quickly widen or narrow the indentation width in
the buffer. This modification does not and will not affect the content of
the file.
What about auto-fill mode?
While tbindent-mode is on, a special function controls the value
of fill-column, adjusting its value to take the visual indentation
modification in the buffer into account. When the auto-fill mode is active,
the text wrapping will occur in the same relative position as it would in the
file with the original space-based indentation.
All customization are part of the tbindent customization group, a child of the
Emacs/Editing/Indent group. The following customizable user options are available:
| User-option | Description |
|---|---|
| tbindent-lighter | The minor mode lighter. Defaults to " ⍈". |
| tbindent-target-indent-widths | The target indentation width to use for specified major
modes. This is an alist of cons (mode . width)
cells. |
| tbindent-target-indent-width-default | The default indentation width used by tbindent-mode when tbindent-target-indent-widths does not hold the value for the current major mode. |
| tbindent-extra-mode-indent-vars | User-specified indentation variable specifications for modes.
|
Manual Activation/Deactivation
Manually toggle the minor mode on/off with M-x tbindent-mode.
The minor mode first checks if the current settings meet the appropriate conditions. It only activates the minor mode if they are. Otherwise it issues a user error describing the problem.
Automatic Activation via major-mode hook
Add tbindent-mode in the hook for the major modes were you want it
activated automatically, by doing something like the following:
For the C programming language major-mode:
(add-hook 'c-mode-common-hook #'tbindent-mode)
For Gleam Tree-Sitter based major-mode:
(add-hook 'gleam-ts-mode-hook #'tbindent-mode)
The library provides the following commands:
| Command | Description |
|---|---|
| tbindent-mode | Toggle the minor mode on/off. |
| tbindent-set-tab-width | Set the tab and indent width used in current buffer to N. Prompts for the width; accepts a value in the [2, 8] range.
|
| Secondary commands: | |
| tbindent-indent-with-tabs | Convert current buffer to use tabs for indentation. Prompts for the width; accepts a value in the [2, 8] range.
|
| tbindent-indent-with-spaces | Convert current buffer back to use space-based indentation.
|
Setup:
- Install dtrt-indent.
tbindent-modeuses dtrt-indent if it is available to detect the indentation scheme used by the file you open. It and sets the value of the indentation control variable for the major mode from inspection of the content of the file. This minimizes chances of mi-interpreting the indentation scheme used by the file.
- Ensure that the hard-coded
tbindent--mode-indent-varsalist has a entry for each of the major modes you want to use.- Add any missing associations to
tbindent-extra-mode-indent-varsby customizing it. - You may also want to create a bug for the missing entry, or create a PR.
- To create a PR, clone the repo, and please create a branch in you repo, make the modification in that branch and create a PR from your branch, not from you main.
- Add any missing associations to
- Customize
tbindent-target-indent-width-default: enter the indentation width you want to see in the buffer for the major modes you want to use.
Test setup:
Once you have done the above, edit a file for one of the mode you have
specified. dtrt-indent will detect the indentation scheme used in the file
and will adjust the value of the indentation control variables accordingly.
Then activate tbindent-mode manually with:
M-x tbindent-mode
Make modifications to the file. Save the changes. Diff your modifications to the files to see that the indentation of your modifications conform to the original indentation scheme of the file.
Execute M-x tbindent-mode again to restore the buffer to the indentation
scheme used by the file.
Activate Hooks:
Once you are happy with the results of your tests you may want to automate
activation of the tbindent-mode in the major modes you use. To do that,
just specify tbindent-mode in a hook for the major mode.
The following shows the corresponding Emacs Lisp code for some major modes.
For C:
(add-hook 'c-mode-common-hook #'tbindent-mode)
For Dart using classic major mode:
(add-hook 'dart-mode-hook #'tbindent-mode)
For Dart using Tree-Sitter based major mode:
(add-hook 'dart-ts-mode-hook #'tbindent-mode)
For Gleam Tree-Sitter based major mode:
(add-hook 'gleam-ts-mode-hook #'tbindent-mode)
- dtrt-indent
- If you intend to work with files written by others, the indentation settings
of those files may well differ from your own. The dtrt-indent
package detects the indentation scheme used by a file and adjust the value
of the indentation control variables accordingly. This is a great help as
the first step in getting ready to use the
tbindent-mode.
- If you intend to work with files written by others, the indentation settings
of those files may well differ from your own. The dtrt-indent
package detects the indentation scheme used by a file and adjust the value
of the indentation control variables accordingly. This is a great help as
the first step in getting ready to use the
- PEL
- The PEL system incorporates code similar to the
tbindent-modeand provides other helpful commands for indentation and other Emacs topics. Thetbindent-modecode was first created as part of PEL and then I extracted this self-sufficient library from it. PEL is a much larger system with a customization-centric configuration and extensive PDF table based documentation. Use a browser that can render PDF (like Firefox) to look at the PEL index PDF and its ⅀ Indentation PDF for more info.
- The PEL system incorporates code similar to the

