A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://plugins.jetbrains.com/docs/intellij/code-formatting.html below:

Code Formatter | IntelliJ Platform Plugin SDK

Code Formatter

The IntelliJ Platform includes a powerful framework for implementing custom language formatters. At its core, the framework represents formatting rules by nested blocks (Block) that cover the whole file. Each block specifies constraints on whitespaces, like indents, wraps, spacing, or alignments. This allows the formatting engine to calculate the smallest number of whitespace modifications necessary to properly format a file.

Introduction

The easiest way to understand how formatting works in practice is to use a small code example in an existing language and use the built-in PSI viewer to inspect how formatting blocks are built. To invoke the builtin PSI viewer with the possibility of inspecting Block Structure, use or .

The image above shows a snippet of code at the top, the PSI structure at the bottom left and the block structure at the bottom right. Like in this example, the structure of blocks is usually built to reflect the PSI structure of the file. In other words, there is a root block that covers the entire file and its nested children cover smaller portions like classes, functions, etc. down to statements, identifiers, or braces. Comparing the PSI and block structure above reveals obvious similarities in the nesting.

In general, however, PSI structure and formatting model are two different things serving different purposes. While the structure of formatting blocks and PSI are usually similar, they do not have to match one-to-one. Additionally, it is vital to understand that the formatter modifies only characters between blocks. Therefore, the tree of blocks must cover all non-whitespace characters the bottom-level, or otherwise, the formatter may delete the characters between blocks. On the other hand, spacing elements should never be covered by blocks unless the space is intended to be left as it is.

Implementation

To format a file or a file fragment, the following steps are required:

FormattingModelBuilder and FormattingModel

When implementing a FormattingModelBuilder, it is common to use the createModel() method and its FormattingContext argument to set the stage for building the formatting blocks with the FormattingModel. Typically, the following steps are part of this preparation:

The IntelliJ Platform already provides concrete implementations of FormattingModel that should be used instead of implementing a custom one. Formatters for custom (programming) languages are usually built so that they mirror the PSI structure of the file. Although not required, this approach is reasonable when thinking about, for example, Java code where the top-level formatting block covers the entire file, its children cover individual classes, blocks on the next level cover methods inside classes, and so on. For these cases, plugins should create a PsiBasedFormattingModel by using FormattingModelProvider.createFormattingModelForPsiFile().

Another implementation of FormattingModel is DocumentBasedFormattingModel. However, in most cases, the PsiBasedFormattingModel should meet the requirements of custom language plugins.

Building the Block Tree

Instead of implementing the Block interface, AbstractBlock should be used as a base class which provides useful default implementations. Although the block implementation of a plugin is specific to the custom language, for the implementation of AbstractBlock some general remarks can be given. It is common to store an instance of the SpacingBuilder which can directly be used when implementing the getSpacing() method by calling the getSpacing() method of the SpacingBuilder.

Much of the work when implementing AbstractBlock goes into implementing buildChildren() that calculates blocks for the children of the current block's AST node. Use getNode().getChildren() to retrieve the AST node's children of the current block, and or each child that is not whitespace, build a subblock which is then added to the list of subblocks. While the exact method of building a subblock highly depends on the specific language, in general, the code for determining the correct Alignment, Indent and Wrap of a block inspects IElementType, checks if the node is in a specific TokenSet or asserts other properties.

The other two more intricate methods that need to be implemented are getChildAttributes() and isIncomplete(). Both are important when determining what indentation to use when Enter is pressed. To calculate the indent for the new line, the formatter engine calls the method getChildAttributes() on either the block immediately before the caret or the parent of that block. This depends on the return value of the isIncomplete() method for the block before the caret. If the block before the cursor is incomplete (contains elements that the user will probably type but has not yet typed, like a closing parenthesis of the parameter list or the trailing semicolon of a statement), getChildAttributes() is called on the block before the caret; otherwise, it's called on the parent block.

As an example, think of contexts (also called blocks) in languages like Java or C that are wrapped in curly braces. If the ASTNode of the current block is such a context or container element, the getChildAttributes() method could return Indent.getNormalIndent() to indent block elements in such a context. Similarly, the isComplete() method could check if for such context elements the first and last child are indeed the open and close curly braces and return false if not.

If the formatting operation doesn't affect the entire file (for example, if the formatter is called to format the pasted block of text), a complete tree of blocks is not built. Rather, only blocks for the text range covered by the formatting operation and their parents are built. Also note that code formatting can be suppressed per region via special comments.

For every block, the plugin specifies the following properties for which several particular use settings exist. These settings have a detailed description in the Javadoc comments for the respective classes.

Indent

Indent specifies how the block is indented relative to its parent block. It provides various factory methods to create different types of indents based on specific formatting settings. There are different modes of indenting defined by factory methods, and the most commonly used are:

If not specified, the default "continuation without first" indent mode is used, meaning the first block is not indented, but later blocks are. The class also allows for configuring indents to be relative to their direct parent block or to enforce parent indents on children starting on a new line. This is useful in complex formatting scenarios, such as aligning blocks within a method call or ensuring consistent indentation in nested structures. Indent also includes methods to create indents with a specific number of spaces and options to control their behavior relative to parent blocks.

Spacing

Spacing indicates what spaces or line breaks are inserted between the specified children of a block. The spacing object specifies the minimum and maximum number of spaces that must be placed between the child blocks. It also specifies the minimum number of line breaks to include and whether the existing line breaks and blank lines should be preserved. The formatting model can also specify that the formatter may not modify the spacing between the specified blocks.

SpacingBuilder helps to build a rule-based configuration for spacing and is an easy way to specify when to put spaces before, after, between, or inside certain elements. The rules typically reflect all possible spacing settings for a language. An example on how to implement such a spacing builder can be found in the Custom Language Support Tutorial: Formatter.

Wrap

The wrap (Wrap) specifies whether the content of the block is wrapped to the next line. Wrapping is performed by inserting a line break before the block content. The plugin can specify that a particular block is never wrapped, always wrapped, or wrapped only if it exceeds the right margin.

Alignment

Alignment is designed to specify which blocks should be aligned with each other in a formatting model. Blocks that return the same Alignment object instance from the getAlignment() method will be aligned together. If two blocks with the same alignment are on different lines and the second block is the first non-whitespace block on its line, the formatter inserts whitespaces before the second block to align it with the first block.

The Alignment includes the Anchor enum with two values, LEFT and RIGHT, determining how the code blocks are aligned. The default alignment is created with the left anchor. To create an alignment instance, the class provides several static methods:

Backward shift allows a former-aligned element to be shifted right to align with a later element. For example, in the code:

int start = 1; int finish = 2;

the = in int start = 1 can be shifted right to align with the = in int finish = 2 if backward shift is allowed.

Additionally, the createChildAlignment() method allows creating an alignment where blocks are aligned based on a base alignment. This is useful for cases where nested alignments are needed, such as aligning a ternary operator with its condition.

Examples Further Tips Non-Whitespace Modifications

Sometimes a plugin requires performing non-whitespace character modifications like reordering methods, changing letter cases, or adding missing braces. The formatting framework provides extension points allowing to achieve these goals.

Pre-Processor

Allows executing additional processing before the actual formatting is performed. For example, it can be used to adjust the formatting range or modify the code by adding, removing, or converting elements like braces, semicolons, quotes, etc. All the introduced changes will be handled by the main formatting step.

To register a formatting pre-processor, a plugin has to provide an implementation of PreFormatProcessor and register it in the com.intellij.preFormatProcessor extension point.

Example: JsonTrailingCommaRemover removing trailing commas in JSON files

Post-Processor

It is similar to the pre-processor but is run after the actual formatting is performed. It can be used for adding, removing, or converting elements like braces, semicolons, quotes, changing letter-cases, etc.

To register a formatting post-processor, a plugin has to provide an implementation of PostFormatProcessor and register it in the com.intellij.postFormatProcessor extension point.

Example: TrailingCommaPostFormatProcessor inserting trailing commas in Kotlin files

Rearranger

Allows custom languages to provide user-configurable arrangement/grouping rules for element types supported by language plugin. Rules can be refined via modifiers and name; ordering can be applied additionally. See Rearranger and related for Javadoc.

Code Style Settings

To set the default indent size for a plugin's language and allow user configuration of tab and indent sizes, implement the FileTypeIndentOptionsProvider interface and register it at the com.intellij.fileTypeIndentOptionsProvider extension point. The return value of createIndentOptions() sets the default indent size.

Example: Custom Language Support Tutorial: Code Style Settings

Restricting Formatting

Use LanguageFormattingRestriction to restrict (automatic) code formatting for given contexts.

External Code Formatter

Register AsyncDocumentFormattingService implementation in the com.intellij.formattingService extension point to invoke external formatter instead of IDE's builtin formatter.

Example: ShExternalFormatter from Shell Script plugin

05 August 2025


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4