Skip to content

fix(formatter): align CL/Elisp indentation with Emacs conventions - #144

Merged
takeokunn merged 6 commits into
mainfrom
feat/emacs-indent-alignment
Aug 8, 2026
Merged

fix(formatter): align CL/Elisp indentation with Emacs conventions#144
takeokunn merged 6 commits into
mainfrom
feat/emacs-indent-alignment

Conversation

@takeokunn

Copy link
Copy Markdown
Collaborator

Summary

Aligns paredit edit format output for Common Lisp and Emacs Lisp with
Emacs/SLIME indentation conventions. 36 divergence patterns fixed across
operator table, layout algorithms, and reindenter.

Changes (13 files, 3 commits)

Wave 1: Operator table + reindenter + general_list alignment

  • Add Elisp dialect routing (elisp_style_for_head()) recognizing 14 operators
  • Add 13 missing CL operators to string-match fallback
  • Fix format_general_list() to align subsequent arguments under the first
    when it shares the head's line (Emacs convention)
  • Expand reindenter BODY_FORMS from 30 to 70 entries

Wave 2: CL if alignment + distinguished column

  • Add ListStyle::IfAligned — all branches at the same column (+2*indent),
    matching Emacs common-lisp-indent-function's (&rest nil) for if
  • Add distinguished_column to format_prefix_body — the child at
    position prefix_len breaks to +4 when it doesn't fit inline

Wave 3: Remaining 7 deferred patterns

  • Route defvar/defparameter/defconstant/defglobal/defstruct/define-condition
    to DefinitionNameBody
  • Route defpackage/in-package/provide/require/use-package/import to General
  • Remove unreachable defpackage entry from string-match fallback
  • Change loop continuation_column from head_end+1 to base+6

Test Results

  • Unit tests: 170 passed
  • CLI integration tests: 112 passed
  • Corpus tests: all passed

- Add Elisp dialect routing (elisp_style_for_head) recognizing 14 operators
  (defvar, defconst, defcustom, save-excursion, while, condition-case, etc.)
- Add 13 missing CL operators to string-match fallback
  (progv, multiple-value-call, return-from, throw, generic-flet, etc.)
- Implement Emacs align-under-first-arg convention in format_general_list
- Expand reindenter BODY_FORMS from 30 to 70 entries covering all
  cl-indent.el specs and Emacs Lisp lisp-mode.el put declarations
…fix_body

- Add ListStyle::IfAligned for Common Lisp if with all branches
  at the same distinguished column (+2*indent), matching Emacs
  common-lisp-indent-function's (&rest nil) convention
- Route CL if to IfAligned, keep Elisp if on ListStyle::If
- Add distinguished_column to format_prefix_body: the child at
  position prefix_len breaks to +4 (distinguished) instead of
  body_column (+2) when it does not fit inline
- Update test expectations for the new if layout
- Route defvar/defconstant/defparameter/defglobal/defstruct/define-condition
  to DefinitionNameBody (name on head line, rest at body-indent)
- Route defpackage/in-package/provide/require/use-package/import to General
- Remove unreachable 'defpackage' entry from string-match fallback (D-8)
- Change loop continuation_column from head_end+1 to base+6 matching
  Emacs default lisp-loop-keyword-indentation
- Update test expectations for define-condition and defvar

Closes all 7 deferred divergence patterns (D-4, D-8 through D-13).
All 41 divergence patterns now resolved (36 fixed, 5 not-applicable).
treefmt-pr-check caught unformatted output on these 3 files.
… sole argument

format_general_list's new "align subsequent siblings under the first
argument" step (this PR) decided whether that first argument fits on
the head line by measuring only the argument's own text. When it is
also the list's last child, the list's own closing delimiter lands on
that same line immediately afterward and was never charged against
the width budget — a compact form exactly one column over the budget
could still be chosen, breaking the documented `<=` boundary
(core-syntax's cjk_width_one_column_over_the_max_width_boundary_wraps).

Reserve one column for the trailing delimiter when the first argument
is also the last child, matching the already-passing
cjk_width_fits_exactly_at_the_max_width_boundary case.

This shifted the CL/Elisp alignment for `list`-shaped calls that break
(subsequent siblings now align under the first argument's column
instead of one column past the opening delimiter, per this PR's own
stated goal), which is why the remove-unused-binding fixtures asserting
that shape's *exact* rewritten text needed their expected strings
updated to match — nextest's fail-fast in CI only surfaced one of these
four; this run used --no-fail-fast to find the rest.
@takeokunn
takeokunn merged commit f404bbd into main Aug 8, 2026
10 checks passed
@takeokunn
takeokunn deleted the feat/emacs-indent-alignment branch August 8, 2026 16:35
@takeokunn takeokunn mentioned this pull request Aug 8, 2026
4 tasks
takeokunn added a commit that referenced this pull request Aug 8, 2026
Minor release: `paredit editor <file>` is a new interactive terminal
editor command (#143); `paredit edit format`/`paredit fix` also align
Common Lisp and Emacs Lisp indentation with Emacs/SLIME conventions
across 36 divergence patterns (#144). No removed or renamed stable
surface — the capabilities catalog diff against v1.5.0 is purely
additive (the new `editor` command only).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant