Skip to content

Additional fixes on top of #2810#2815

Open
yakobowski wants to merge 7 commits into
ocaml-ppx:mainfrom
yakobowski:fix-2810-standalone
Open

Additional fixes on top of #2810#2815
yakobowski wants to merge 7 commits into
ocaml-ppx:mainfrom
yakobowski:fix-2810-standalone

Conversation

@yakobowski

Copy link
Copy Markdown

This MR fixes a few additional problems for the if-then-else=fit-or-vertical mode. It is stacked on top of #2180.

The first commit partially reverts some changes (bare_branch logic) in order to fix some additional less-than-ideal reformatting found on our codebase. I have added testcases, but I was not able to add the more interesting ones in a standalone commit, as ocamlformat without the fix does not stabilize in some configurations. (See the last commit.)

MisterDA and others added 7 commits June 24, 2026 08:37
The `Compact` and `Fit_or_vertical` arms of `get_if_then_else` each carried
a near-identical block computing `branch_pro` for the case of a comment
preceding a special branch body, and their inline break expressions
(`break 1000 0 $ cmts $ break 1000 0` and `break 1000 2 $ cmts`) duplicated
exactly what `raw_cmts_branch_pro` already computes per mode.

Extract a single `branch_pro_with_cmts ~default ~guard` helper that reuses
`raw_cmts_branch_pro`, parameterized by each mode's comment-less `branch_pro`
and its extra applicability guard. Behavior is unchanged (the full test suite
passes with no ref changes).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
With `if-then-else=fit-or-vertical`, an `if … else (* cmt *) match … with`
branch (a bare match/function/try/if, not wrapped in begin/end or
parentheses) rendered the branch body indented to the comment's end
column instead of under the comment.

`Params.raw_cmts_branch_pro` emitted, for non-Compact modes,
`break 1000 2 $ cmts` with no trailing break. A bare branch's body breaks
with `break_unless_newline`, a no-op unless already at the beginning of a
line, and `fmt_match` opens its box at the current column — the comment's
end column. Add an optional `~bare_branch` flag that appends a trailing
break so the body box opens at the line start. It is not set for begin/end-
or paren-wrapped branches, which emit their opening delimiter right after
the comment, keeping that rendering unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
With `if-then-else=fit-or-vertical`, an `else begin match … with … end`
branch rendered the header split over several lines (`begin match` /
scrutinee / `with`), `begin if … end` likewise, and `end` ended up less
indented than `begin`.

For the `begin match/try/function/if end` shortcut, `fmt_beginend` threaded
the branch `pro` into the inner expression's `pro`. In fit-or-vertical (and
vertical) that `pro` is a `break_unless_newline 1000`, and because the inner
expression's ctx0 is the begin/end node, `match_inner_pro` places it inside
the header box. `pp_print_or_newline` enqueues its full width (1000) as the
token length, so the header box never fits and is forced to break.

When the begin/end is an if-then-else branch, emit `pro` outside the box (the
branch break already positions it) and wrap the body and `end` together so
`end` lines up with `begin`. Other contexts (e.g. `map x begin fun … end`)
keep `pro` inside to preserve their indentation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reworks the previous fix (e3babbb) per review. The earlier version
special-cased `ctx0=ifthenelse` in `fmt_beginend` to handle a comment
before a `begin match/if … end` branch body; it glued the body
(`begin if a then b`) and reindented it when a comment was added.

Why the comment case now goes through the plain begin/end path:
`fmt_beginend` cannot see the comment — `branch_pro` consumes it in
`Params` before the expression is formatted. `get_if_then_else`, however,
runs before consumption, so the decision belongs there. A special
`begin match/if … end` branch with a leading comment on its body no
longer takes the `begin <kw>` shortcut arm; it falls through to the
existing plain begin/end machinery (`has_cmts_before` predicate). That
keeps `begin` on its own line with the body (comment included) one indent
in, identically across all if-then-else modes, so adding a comment no
longer reindents the body.

Why expr_pro is dropped for special-bodied begin/end branches: the
fit-or-vertical / vertical branch `expr_pro` is `break_unless_newline
1000`, which enqueues width 1000 even as a no-op at BOL and poisons the
body's `match … with` / `if … then` header box, forcing it to split over
several lines. Such a body already breaks after `begin`, so it provides
its own break instead of the poisoning one. Simple-bodied `begin e end`
keeps the regular branch break (no header to poison). K&R additionally
dropped the extra body box that indented a comment-routed body by +2.

Why a ctx0 box wrap remains in fmt_beginend: the no-comment shortcut path
still needs `end` aligned with `begin`. In fit-or-vertical the branch box
is `hovbox 0` while the branch break indents `begin` one level in, so the
body and `end` are wrapped together. This is scoped to `ctx0=ifthenelse`
because wrapping unconditionally reindents application arguments
(`map x begin fun … end`, exp_grouping.ml); the scope is structural
(alignment), not comment-handling logic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When a comment precedes a parenthesized match/try/function/if branch
(janestreet and ahrefs profiles with parens_ite or exp_grouping=parens),
glue the opening paren to the content instead of putting it alone on a
line.  Before: `(* cmt *)\n(\nmatch e with\n| ...)`
After:  `(* cmt *)\n(match e with\n | ...)`

Also simplify raw_cmts_branch_pro: remove the bare_branch distinction
(always emit trailing break), since paren-glue now handles the case that
bare_branch was partially covering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests the paren-glue behavior: with exp_grouping=parens (janestreet,
ahrefs), [begin match ... end] is parsed as a bare match needing
parenthesization; a preceding comment must not glue to the opening
paren.

Without the fix, this renders as:
    (* a comment *)begin match some_long_scrutinee_expression with
                   | A -> a
                   | B -> b
                   end
or (with exp_grouping=parens):
    (* a comment *)
    (
    match some_long_scrutinee_expression with
    | A -> a
    | B -> b)

Note: without the preceding fix, janestreet + k-r mode has a
pre-existing over-indentation issue for match-in-parens branches (match
indented +2 relative to the comment inside the parens).  This caused
non-convergence with max-iters=3 in the unfixed code; the paren-glue
fix resolves the oscillation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

2 participants