Skip to content

pierre-rouleau/tab-based-indent

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tab-Based Indentation Converter

Melpa
  • 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.

res/tbindent.gif

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 shows dtrt-indent-mode detecting the indentation of the file and tbindent-mode taking over.
  • Open a terminal shell buffer in the bottom window.
    • inside the shell execute vi and open the same file inside Vim (which runs in a terminal running inside Emacs).
  • 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.

res/tbindent-vi.gif

Overview

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-vars or from the user-specified tbindent-extra-mode-indent-vars.
    • It's strongly recommended to install the dtrt-indent package, allowing tbindent-mode to activate``dtrt-indent-mode`` before checking the indentation control variable, minimizing changes of misinterpretation of the file's indentation scheme.
  • the value of tab-width must be equal to the indentation value identified by the indentation control variable used for the major-mode, such as, for example:
    • c-basic-offset in C, C++ , D buffers, or
    • ada-indent for Ada buffers, or
    • python-indent-offset for Python buffers.
  • Those values must also represent the real indentation width used inside the file.

When turning on:

  • tbindent-mode checks if the indentation variable for the major mode is known and issue an error with instructions if it is unknown.
  • tbindent-mode compares the value of tab-width with the value of the indentation control variable. If they differ, and if dtrt-indent is available, it sets the local value of tab-width to the value of the indentation control variable and issues a message. If dtrt-indent is not available tbindent-mode issues 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-width and 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-column value (more on this later).
  • 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.

Customization

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.

  • This is a alist mapping the major mode name to the name of one variable, a list of variables or a list of (vars . offset). This identifies the name of the indentation control variable or variables used by the mode and if necessary an width offset applied to the variable: var = width + offset.
  • By adding entries into this list, you can add information that complements or overrides the entries in the hard-coded tbindent--mode-indent-vars.
  • For example if you use the old ada-mode you may want to add the entry that maps it to ada-indent variable by adding ada-mode to the list of modes, associating it with ada-indent indentation control variable.

Using tbindent mode

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)
    

tbindent Commands

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.

  • Set the buffer local value of tab-width and indent control variable(s) used by the current buffer.
  • Use this command to change the indentation width when tbindent-mode is active.
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.

  • A utility command, used internally by tbindent-mode. Not available independently when tbindent-mode is active. Use it when tbindent-mode is turned off to manually convert the space-based indentation into tabs-based indentation scheme.
tbindent-indent-with-spaces

Convert current buffer back to use space-based indentation.

  • A utility command, used internally by tbindent-mode. Not available independently when tbindent-mode is active. Use it when tbindent-mode is turned off, and after you used tbindent-indent-with-tabs to convert the tabs-based indented buffer back into space-based indented scheme.

Recommended Setup To Automate use of tbindent

Setup:

  • Install dtrt-indent.
    • tbindent-mode uses 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-vars alist has a entry for each of the major modes you want to use.
    • Add any missing associations to tbindent-extra-mode-indent-vars by 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.
  • 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)

Other Packages

  • 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.
  • PEL
    • The PEL system incorporates code similar to the tbindent-mode and provides other helpful commands for indentation and other Emacs topics. The tbindent-mode code 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.

About

Edit space-based indented files as tab-based; control indentation width while editing.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published