Formatting

Preview Feature: The formatter is currently in preview. We'd love feedback from early adopters! Please report any issues or unexpected output at GitHub Issues.

Known Limitations

The language server provides SQL formatting that produces consistent, readable code. Built on Postgres' own parser, the formatter ensures 100% syntax compatibility with your SQL.

Comments are not processed during formatting. They get removed before formatting and restored after.

Configuration

Configure formatting behavior in your postgres-language-server.jsonc:

{
  "format": {
    "enabled": true,
    "lineWidth": 100,
    "indentSize": 2,
    "indentStyle": "spaces",
    "keywordCase": "lower",
    "constantCase": "lower",
    "typeCase": "lower",
    "functionArgumentGroups": {
      "my_pair_func": 2
    }
  }
}

Options

Option Default Description
enabled true Enable or disable the formatter
lineWidth 100 Maximum line width before breaking
indentSize 2 Number of spaces (or tab width) for indentation
indentStyle "spaces" Use "spaces" or "tabs" for indentation
keywordCase "lower" Casing for SQL keywords: "upper" or "lower"
constantCase "lower" Casing for constants (NULL, TRUE, FALSE): "upper" or "lower"
typeCase "lower" Casing for data types (text, int, varchar): "upper" or "lower"
commaStyle "trailing" Where a comma sits when a list breaks: "trailing" or "leading"
logicalOperatorPlacement "trailing" Where AND and OR sit when a condition breaks: "trailing" or "leading"
layout "fit" How a statement is laid out: "fit" breaks only past the line width, "expanded" always breaks between clauses
castStyle "cast" How an explicit cast is spelled: "cast" for CAST(x AS t), "operator" for x::t
clauseBodyStyle "break" Where a clause body starts: "break" for a new line, "compact" to keep the first element on the keyword line
isolateSemicolon false Put the terminating semicolon on its own line when the statement spans several lines
functionArgumentGroups {} (plus built-in defaults, see below) Function names mapped to the number of adjacent arguments that should stay together when wrapping

functionArgumentGroups matches the final, unqualified function name case-insensitively. A group size of 2 is useful for functions whose arguments form key/value pairs:

select jsonb_build_object(
  'amountTTC', to_amount(amount_ttc, invoices.currency),
  'amountVAT', to_amount(amount_vat, invoices.currency)
);

A group stays on one line when it fits; when it does not, the formatter breaks between its arguments rather than exceeding the line width.

json_build_object and jsonb_build_object are grouped in pairs by default, without configuration. Configure a function explicitly to use a different group size; a size of 1 opts out of the built-in default:

{
  "format": {
    "functionArgumentGroups": {
      "jsonb_build_object": 1
    }
  }
}

Example Output

With default settings (lowercase):

create table users (
  id serial primary key,
  name text not null,
  active boolean default true
);

select * from users where active = true;

With uppercase keywords and constants:

CREATE TABLE users (
  id serial PRIMARY KEY,
  name text NOT NULL,
  active boolean DEFAULT TRUE
);

SELECT * FROM users WHERE active = TRUE;

CLI Usage

Format files using the CLI:

# Format and show diff
postgres-language-server format file.sql

# Format and write changes
postgres-language-server format file.sql --write

# Format entire directory
postgres-language-server format migrations/ --write

Editor Integration

The formatter integrates with your editor via the Language Server Protocol. Use your editor's format document command (typically bound to a keyboard shortcut) to format SQL files.

Ignoring Files

Use the ignore and include options to control which files are formatted:

{
  "format": {
    "ignore": ["**/generated/**", "**/vendor/**"],
    "include": ["**/*.sql"]
  }
}