chore: more work done and planning with AI.
This commit is contained in:
@@ -1,26 +1,68 @@
|
||||
# PgTidy house style — all fields shown with their default values.
|
||||
# PgTidy house style — all fields with their default values.
|
||||
# Place as .pgtidy.yaml in your project root (or any parent directory).
|
||||
# Any field you omit keeps its default.
|
||||
|
||||
# Indentation string for one level (two spaces).
|
||||
indent: " "
|
||||
# --- Core ---
|
||||
|
||||
# Line terminator written by the formatter.
|
||||
newline: "\n"
|
||||
indent: " " # One indentation level (two spaces).
|
||||
newline: "\n" # Line terminator emitted by the formatter.
|
||||
|
||||
# Casing for SQL keywords (SELECT, FROM, WHERE, …).
|
||||
# --- Casing ---
|
||||
# upper | lower | preserve
|
||||
keyword_case: upper
|
||||
|
||||
# Casing for unquoted identifiers (column names, variable names, …).
|
||||
# upper | lower | preserve
|
||||
ident_case: lower
|
||||
keyword_case: upper # SQL keywords (SELECT, FROM, WHERE, …)
|
||||
ident_case: lower # Unquoted identifiers (column/variable names)
|
||||
type_case: lower # Built-in type names (text, integer, boolean, …)
|
||||
alias_case: lower # Token immediately following AS in SELECT / FROM
|
||||
builtin_case: lower # Built-in function names (COALESCE, MAX, NOW, …)
|
||||
custom_type_case: lower # User-defined / domain types not in the built-in set
|
||||
|
||||
# Casing for built-in type names (text, integer, boolean, …).
|
||||
# upper | lower | preserve
|
||||
type_case: lower
|
||||
# --- Query layout ---
|
||||
|
||||
# Comma placement in multi-line parameter / column lists.
|
||||
# leading → comma at the start of the continuation line (,col)
|
||||
# trailing → comma at the end of the preceding line (col,)
|
||||
commas: leading
|
||||
commas: leading # leading → ,col | trailing → col,
|
||||
|
||||
align_columns: false # Pad SELECT list items so values align vertically
|
||||
align_line_comments: false # Align trailing -- comments within a block
|
||||
select_align_as: false # Pad between expression and AS keyword in SELECT list
|
||||
set_align_equal: false # Align = in UPDATE SET list
|
||||
indent_join: false # Extra indentation for JOIN … ON lines
|
||||
join_indent_size: 1 # Number of extra indent levels for JOINs
|
||||
|
||||
# always | when_long | never
|
||||
where_wrap: always # Each AND/OR condition on its own line
|
||||
|
||||
where_and_or_indent: true # AND/OR indented one level under WHERE
|
||||
|
||||
# --- Subqueries ---
|
||||
# same_line | new_line
|
||||
|
||||
subquery_opening: same_line # Opening ( placement
|
||||
subquery_content: new_line # Content indentation inside parens
|
||||
subquery_closing: new_line # Closing ) placement
|
||||
subquery_space_before_paren: false # Space before ( in subqueries
|
||||
|
||||
# --- INSERT ---
|
||||
|
||||
insert_collapse_values: true # Fold multiple VALUES rows onto fewer lines
|
||||
|
||||
# --- Routines (functions / procedures) ---
|
||||
|
||||
align_param_types: true # Pad param names so type column aligns across all params
|
||||
routine_as_wrap: true # Newline before AS $$ (false = keep AS on same line)
|
||||
|
||||
# --- PL/pgSQL body ---
|
||||
|
||||
plpgsql_max_blank_lines: 1 # Max consecutive blank lines in body
|
||||
plpgsql_declare_align_type: false # Align type column in DECLARE block
|
||||
plpgsql_declare_align_eq: false # Align := / = in DECLARE block
|
||||
plpgsql_if_then_newline: true # THEN on its own line (false = same line as condition)
|
||||
plpgsql_loop_collapse: true # Collapse empty loop bodies to one line
|
||||
|
||||
# --- Expressions ---
|
||||
|
||||
binary_op_align: false # Align =, <>, || etc. vertically in WHERE/expression lists
|
||||
space_after_comma_in_calls: false # Space after , in function calls: func(a, b)
|
||||
case_when_wrap: false # Each WHEN … THEN on its own line
|
||||
case_end: new_line # END placement: same_line | new_line
|
||||
case_collapse: false # Collapse short CASE expressions to one line
|
||||
record_space_before_paren: false # Space before ( in ROW(…) / record constructors
|
||||
|
||||
@@ -71,6 +71,89 @@ to the intended style below.
|
||||
`::`, `->`, `->>`, array `[...]`, or before a call's `(`.
|
||||
- Dollar-quote tags preserved verbatim (`$$`, `$S$`, `$Z$`, …).
|
||||
|
||||
## DataGrip settings mapping
|
||||
|
||||
`PostgresCodeStyleSettings` (DataGrip / JetBrains) is the reference for all configurable
|
||||
style options. The table below maps every relevant DataGrip key to its `.pgtidy.yaml`
|
||||
counterpart so a user can reproduce their DataGrip style exactly in PgTidy.
|
||||
|
||||
DataGrip enum conventions used below:
|
||||
- **Case**: 0=preserve, 1=upper, 2=lower
|
||||
- **Comma**: 1=leading (`,col`), 2=trailing (`col,`)
|
||||
- **Placement**: 1=same_line, 2=new_line
|
||||
- **Wrap**: 0=never, 1=when_long, 2=always
|
||||
|
||||
### Casing
|
||||
|
||||
| DataGrip key | PgTidy key | Default | Notes |
|
||||
|---|---|---|---|
|
||||
| `KEYWORD_CASE` | `keyword_case` | `upper` | SELECT, FROM, WHERE, … |
|
||||
| `IDENTIFIER_CASE` | `ident_case` | `lower` | unquoted column/variable names |
|
||||
| `TYPE_CASE` | `type_case` | `lower` | built-in type names (text, integer, …) |
|
||||
| `CUSTOM_TYPE_CASE` | `custom_type_case` | `lower` | user-defined / domain types |
|
||||
| `ALIAS_CASE` | `alias_case` | `lower` | column and table aliases |
|
||||
| `BUILT_IN_CASE` | `builtin_case` | `lower` | built-in functions (COALESCE, MAX, …) |
|
||||
|
||||
### Query layout
|
||||
|
||||
| DataGrip key | PgTidy key | Default | Notes |
|
||||
|---|---|---|---|
|
||||
| `QUERY_EL_COMMA` | `commas` | `leading` | applies to all clause element lists |
|
||||
| `QUERY_ALIGN_ELEMENTS` | `align_columns` | `false` | align SELECT list items to same column |
|
||||
| `QUERY_ALIGN_LINE_COMMENTS` | `align_line_comments` | `false` | align `--` inline comments in a block |
|
||||
| `SELECT_ALIGN_AS` | `select_align_as` | `false` | align `AS` keyword across SELECT list |
|
||||
| `FROM_INDENT_JOIN` | `indent_join` | `false` | indent JOIN relative to FROM |
|
||||
| `FROM_ONLY_JOIN_INDENT` | `join_indent_size` | `1` | extra indent levels for JOINs |
|
||||
| `SET_ALIGN_EQUAL_SIGN` | `set_align_equal` | `false` | align `=` in UPDATE SET list |
|
||||
| `WHERE_EL_WRAP` + `WHERE_EL_LINE` | `where_wrap` | `always` | always \| when_long \| never — each AND/OR condition on its own line |
|
||||
| _(no DataGrip equivalent)_ | `where_and_or_indent` | `true` | when true, AND/OR are indented one level under WHERE, not at WHERE's column |
|
||||
|
||||
### Subqueries
|
||||
|
||||
| DataGrip key | PgTidy key | Default | Notes |
|
||||
|---|---|---|---|
|
||||
| `SUBQUERY_OPENING` | `subquery_opening` | `same_line` | opening `(` placement |
|
||||
| `SUBQUERY_CONTENT` | `subquery_content` | `new_line` | content indentation inside paren |
|
||||
| `SUBQUERY_CLOSING` | `subquery_closing` | `new_line` | closing `)` placement |
|
||||
| `SUBQUERY_PAR_SPACE_BEFORE` | `subquery_space_before_paren` | `false` | space before `(` |
|
||||
|
||||
### INSERT
|
||||
|
||||
| DataGrip key | PgTidy key | Default | Notes |
|
||||
|---|---|---|---|
|
||||
| `INSERT_COLLAPSE_MULTI_ROW_VALUES` | `insert_collapse_values` | `true` | fold VALUES rows into fewer lines |
|
||||
|
||||
### Routine (function / procedure)
|
||||
|
||||
| DataGrip key | PgTidy key | Default | Notes |
|
||||
|---|---|---|---|
|
||||
| `ROUTINE_ARG_COMMA` | uses `commas` | `leading` | same setting as query lists |
|
||||
| `ROUTINE_ARG_ALIGN_TYPES` | `align_param_types` | `true` | align type column in param list |
|
||||
| `ROUTINE_AS_WRAP` | `routine_as_wrap` | `true` | newline before `AS $$` |
|
||||
|
||||
### PL/pgSQL body
|
||||
|
||||
| DataGrip key | PgTidy key | Default | Notes |
|
||||
|---|---|---|---|
|
||||
| `IMP_COMMON_KEEP_BLANK_LINES_IN_CODE` | `plpgsql_max_blank_lines` | `1` | max consecutive blank lines in body |
|
||||
| `IMP_DECLARE_ALIGN_TYPE` | `plpgsql_declare_align_type` | `false` | align type column in DECLARE block |
|
||||
| `IMP_DECLARE_ALIGN_EQ` | `plpgsql_declare_align_eq` | `false` | align `:=` / `=` in DECLARE block |
|
||||
| `IMP_IF_THEN_WRAP_THEN` | `plpgsql_if_then_newline` | `true` | THEN on its own line |
|
||||
| `IMP_LOOP_COLLAPSE` | `plpgsql_loop_collapse` | `true` | collapse empty loop bodies |
|
||||
|
||||
### Expressions
|
||||
|
||||
| DataGrip key | PgTidy key | Default | Notes |
|
||||
|---|---|---|---|
|
||||
| `EXPR_BINARY_OP_ALIGN` | `binary_op_align` | `false` | align `=`, `<>`, `||`, … vertically in WHERE/expression lists; default false — must not be hardcoded |
|
||||
| `EXPR_CALL_SPACE_AFTER_COMMA` | `space_after_comma_in_calls` | `false` | space after `,` in function calls |
|
||||
| `EXPR_CASE_WHEN_WRAP` | `case_when_wrap` | `false` | each WHEN on its own line |
|
||||
| `EXPR_CASE_END` | `case_end` | `new_line` | same_line \| new_line |
|
||||
| `EXPR_CASE_COLLAPSE` | `case_collapse` | `false` | collapse short CASE to one line |
|
||||
| `CORTEGE_SPACE_BEFORE_L_PAREN` | `record_space_before_paren` | `false` | space before `(` in ROW/record constructors |
|
||||
|
||||
---
|
||||
|
||||
## Milestones
|
||||
|
||||
### V1 — Formatter + CLI (priority)
|
||||
@@ -88,6 +171,8 @@ to the intended style below.
|
||||
5. **CLI** (`cmd/pgtidy fmt`): `--check`, `--write`/`-w`, stdin→stdout, `--diff`; config
|
||||
discovery walking up to `.pgtidy.yaml`; CI-friendly exit codes.
|
||||
6. **Config** (`pkg/config`): load/merge style config; defaults = house style above.
|
||||
Full field set defined in the DataGrip settings mapping section above — covers casing
|
||||
(6 keys), query layout, subqueries, INSERT, routines, PL/pgSQL body, and expressions.
|
||||
|
||||
**Safety guarantees (tested):** semantic equivalence (re-lex output, compare non-trivia token
|
||||
stream to input), and idempotence (`fmt(fmt(x)) == fmt(x)`). The corpus is the
|
||||
|
||||
@@ -161,6 +161,83 @@ Legend: ✅ done · 🚧 in progress · ⬜ not started
|
||||
- _Still TODO: Wadler Doc-IR printer for width-aware wrapping of long lines._
|
||||
- _Still TODO: LSP range formatting._
|
||||
|
||||
## ✅ Config expansion — DataGrip settings parity
|
||||
|
||||
Reference: `PostgresCodeStyleSettings` mapping in `docs/plan.md`.
|
||||
|
||||
### ✅ Extended `pkg/config` fields
|
||||
|
||||
Added to `Style` struct, `yamlFile`, and `Load()` in `pkg/config/config.go`:
|
||||
|
||||
- New types: `WrapMode` (`always`|`when_long`|`never`), `Placement` (`same_line`|`new_line`)
|
||||
- **Casing**: `AliasCase`, `BuiltinCase`, `CustomTypeCase` — all default `lower`
|
||||
- **Query layout**: `AlignColumns`, `AlignLineComments`, `SelectAlignAs`, `SetAlignEqual`,
|
||||
`IndentJoin`, `JoinIndentSize`, `WhereWrap`, `WhereAndOrIndent`
|
||||
- **Subqueries**: `SubqueryOpening`, `SubqueryContent`, `SubqueryClosing`, `SubquerySpaceBeforeParen`
|
||||
- **INSERT**: `InsertCollapseValues`
|
||||
- **Routines**: `AlignParamTypes`, `RoutineAsWrap`
|
||||
- **PL/pgSQL**: `PlpgsqlMaxBlankLines`, `PlpgsqlDeclareAlignType`, `PlpgsqlDeclareAlignEq`,
|
||||
`PlpgsqlIfThenNewline`, `PlpgsqlLoopCollapse`
|
||||
- **Expressions**: `BinaryOpAlign`, `SpaceAfterCommaInCalls`, `CaseWhenWrap`, `CaseEnd`,
|
||||
`CaseCollapse`, `RecordSpaceBeforeParen`
|
||||
- `docs/config/default.pgtidy.yaml` updated with all new keys and comments.
|
||||
|
||||
### ✅ Casing engine — alias and built-in classification
|
||||
|
||||
`pkg/format/keywords.go`: added `builtinFunctions` set (COALESCE, MAX, MIN, NOW, …).
|
||||
`pkg/format/format.go`: `caseTextCtx` uses context — `prev` token and `nextIsLParen` flag
|
||||
to route ident tokens through `AliasCase` (after AS) or `BuiltinCase` (before `(`).
|
||||
`inline()` and `dmlInline()` pass context to `caseTextCtx`.
|
||||
|
||||
### ✅ Formatter — query layout settings (`pkg/format/dml.go`)
|
||||
|
||||
- `indent_join` + `join_indent_size`: JOIN clause indented by `JoinIndentSize × Indent`.
|
||||
- `where_wrap` + `where_and_or_indent`: `dmlWhereClause` splits AND/OR conditions; `always`
|
||||
puts each condition on its own line indented under WHERE; `never` keeps inline.
|
||||
- `set_align_equal`: `dmlColListSet` pads LHS of SET items so `=` signs align.
|
||||
- `align_columns` + `select_align_as`: `dmlColListSelect` + `alignSelectItems` pads
|
||||
SELECT expressions so AS keywords and aliases align vertically.
|
||||
- `space_after_comma_in_calls` applied in `dmlInline`.
|
||||
- `binary_op_align` registered in config (enforcement in WHERE/expression context deferred).
|
||||
|
||||
### ⬜ Formatter — subquery formatting
|
||||
|
||||
`subquery_opening/content/closing/space_before_paren` fields are wired in config.
|
||||
Enforcement in `dml.go` is not yet implemented — subqueries use current CTE formatting
|
||||
as a proxy (new_line for content, inline for single-arg subexpressions).
|
||||
|
||||
### ⬜ Formatter — INSERT VALUES collapse
|
||||
|
||||
`insert_collapse_values` field is wired in config. Enforcement in `dml.go` not yet implemented.
|
||||
|
||||
### ✅ Formatter — routine param alignment (`pkg/format/format.go`)
|
||||
|
||||
- `align_param_types`: `alignParamTypes()` pads param names so type columns align; default `true`.
|
||||
- `routine_as_wrap`: when `false`, AS stays on the same line as the last option clause.
|
||||
- Golden file `testdata/corpus/test_a.pgsql` updated to reflect aligned params.
|
||||
|
||||
### ✅ Formatter — PL/pgSQL body settings (`pkg/format/body.go`)
|
||||
|
||||
- `plpgsql_max_blank_lines`: blank-line runs capped at the configured limit; default `1`.
|
||||
- `plpgsql_declare_align_type` + `plpgsql_declare_align_eq`: two-pass declare formatter
|
||||
measures name/type widths then pads for alignment; `writeDeclareAligned` helper.
|
||||
- `plpgsql_if_then_newline`: when `false`, `joinThenToCondition` merges THEN onto the
|
||||
preceding condition line.
|
||||
- `plpgsql_loop_collapse`: `tryCollapseLoop` detects empty FOR/WHILE loop bodies and
|
||||
collapses them to one line.
|
||||
- CRLF normalization in trivia emission (comment text, body trivia before DECLARE).
|
||||
|
||||
### ⬜ Formatter — expression settings (case_when_wrap, case_end, case_collapse, record_space_before_paren)
|
||||
|
||||
Config fields wired. Expression-level CASE/ROW formatting not yet implemented.
|
||||
|
||||
### ⬜ DataGrip XML import/export (optional, V4+)
|
||||
|
||||
`pgtidy config import --datagrip <settings.xml>` / `pgtidy config export --datagrip`
|
||||
not implemented.
|
||||
|
||||
---
|
||||
|
||||
## Open risks
|
||||
- `go-pgquery` tracks PG17 (not PG18) — fine for lint; irrelevant to formatter path.
|
||||
- Leading-comma + one-per-line is a first-class style option, not an afterthought.
|
||||
|
||||
Reference in New Issue
Block a user