Skip to content

fix: keep position: 'middle' within the column budget - #32

Merged
sindresorhus merged 2 commits into
sindresorhus:mainfrom
spokodev:fix-middle-space-overflow
Jun 23, 2026
Merged

fix: keep position: 'middle' within the column budget#32
sindresorhus merged 2 commits into
sindresorhus:mainfrom
spokodev:fix-middle-space-overflow

Conversation

@spokodev

Copy link
Copy Markdown
Contributor

With position: 'middle', the budget is split with Math.floor(columns / 2) without reserving room for the truncation character. When the character does not fit in the remaining space (most visibly with space: true, where it becomes , width 3), the right slice clamps to empty while the left slice plus the character already exceed columns:

cliTruncate('unicorns', 2, {position: 'middle', space: true}); // 'u … '  width 4 (budget 2)
cliTruncate('unicorns', 3, {position: 'middle', space: true}); // 'u … '  width 4 (budget 3)
cliTruncate('unicorns', 4, {position: 'middle', space: true}); // 'un … ' width 5 (budget 4)

This breaks the documented invariant that the result occupies at most columns columns (position: 'end'/'start' stay within budget at the same widths).

The fix caps the left slice at columns - truncationWidth, so the two slices plus the truncation character never exceed columns, and drops the padding spaces when the padded character itself would not fit (so some text can still be shown). Normal widths are unchanged — columns: 7 still yields uni … s. Verified width <= columns across ASCII and wide-character text for every position and space setting (264 combinations); existing wide-char test (issue #28) still passes. Added regression assertions.

With `position: 'middle'`, the budget was split with
`Math.floor(columns / 2)` without reserving room for the truncation
character. When the character did not fit in the remaining space (most
visibly with `space: true`, where it becomes ` … `, width 3), the right
slice clamped to empty while the left slice plus the character already
exceeded `columns`:

    cliTruncate('unicorns', 2, {position: 'middle', space: true})
    // 'u … '  width 4, over the budget of 2

Cap the left slice at `columns - truncationWidth` so the two slices plus
the character never exceed `columns`, and drop the padding spaces when the
padded character itself would not fit. Normal widths are unchanged
(`columns: 7` still yields `uni … s`); verified width <= columns across
ASCII and wide-character text for every position and `space` setting.
@sindresorhus

Copy link
Copy Markdown
Owner

I found one case this still misses: the new width reservation does not cover the preferTruncationOnSpace branch. With position: 'middle', space: true, and preferTruncationOnSpace: true, the result can still exceed columns:

cliTruncate('unicorns', 4, {position: 'middle', space: true, preferTruncationOnSpace: true});
//=> 'u … ns' // width 6, budget 4

That branch still seems to use a hard-coded + 1 when finding the right slice, so it should use the actual truncation width too. Please add a regresion test for this option combo.

The `position: 'middle'` + `preferTruncationOnSpace` branch computed the
right-hand break point with a hard-coded `+ 1`, so with `space: true`
(truncation character ` … `, width 3) the result could exceed `columns`,
e.g. `cliTruncate('unicorns', 4, {position: 'middle', space: true,
preferTruncationOnSpace: true})` returned `'u … ns'` (width 6).

Use the actual `truncationWidth`, matching the non-prefer branch.
`getIndexOfNearestSpace` only searches rightward here, so it can shorten
the slice but never extend it past the budget. Added a regression test for
the option combo.
@spokodev

Copy link
Copy Markdown
Contributor Author

Good catch, thanks. Fixed: the prefer-on-space middle branch now reserves the actual truncationWidth for the right-hand break point instead of the hard-coded + 1, so cliTruncate('unicorns', 4, {position: 'middle', space: true, preferTruncationOnSpace: true}) is now 'u … ' (width 4). getIndexOfNearestSpace only searches rightward here, so it can only shorten the slice, never push it past the budget. Added a regression test for the middle + space + preferTruncationOnSpace combo (including a width-<= loop over small columns).

@sindresorhus
sindresorhus merged commit f87490f into sindresorhus:main Jun 23, 2026
2 checks passed
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