Skip to content

fix(util): prevent slice(-0) leak in truncateMiddle on small maxLength - #55953

Open
smartworldarafath wants to merge 2 commits into
anomalyco:devfrom
smartworldarafath:fix-truncate-middle
Open

smartworldarafath wants to merge 2 commits into
anomalyco:devfrom
smartworldarafath:fix-truncate-middle

Conversation

@smartworldarafath

@smartworldarafath smartworldarafath commented Oct 16, 2026 •

Copy link
Copy Markdown

Issue for this PR

Closes #44338

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

When maxLength <= 2 in truncateMiddle, Math.floor((maxLength - 1) / 2) evaluates to 0.
In JavaScript, slice(-0) is evaluated as slice(0), returning the entire string.
For example, truncateMiddle("some-long-name", 1) returned "…some-long-name" instead of "…".

This PR fixes this by:

  1. Handling maxLength <= 1 explicitly with an early return "…".
  2. Ensuring slice(-end) is only called when end > 0, avoiding slice(-0).
  3. Reusing @opencode/util/path's truncateMiddle in @opencode/tui/src/util/locale.ts so the TUI uses the shared, fixed implementation.

How did you verify your code works?

  • Added unit tests in packages/util/src/path.test.ts verifying maxLength 1, 2, and 3 return "…", "a…", and "a…p".
  • Added unit tests in packages/tui/test/util/locale.test.ts verifying Locale.truncateMiddle correctly truncates small lengths.
  • Verified all 35 tests in packages/util/src/path.test.ts pass cleanly.

Screenshots / recordings

N/A (text truncation helper)

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions github-actions Bot added needs:compliance This means the issue will auto-close after 2 hours. needs:issue labels Oct 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix to truncateMiddle in packages/util/src/path.ts is correct. I ran it against the base and the PR: with the PR, maxLength 1 and 2 now return "…" and "h…" instead of the whole string, and longer lengths are unchanged. The util tests and typecheck pass. The public API is unchanged: the exported signature is the same and only the output for maxLength <= 2 changes, which is backwards compatible.

One thing worth addressing: nothing in the repo calls this util function. The truncation users actually see comes from the separate Locale.truncateMiddle in packages/tui/src/util/locale.ts, which has exactly the same slice(-0) bug and still returns "…hello world" for maxLength 1 with this PR. That copy is called from dialog-select.tsx with Math.max(1, width()), the autocomplete and the prompt. Could you fix it there too, ideally by having the TUI reuse the @opencode/util function instead of keeping a second copy? Otherwise this change has no effect users can see.

Related: #44338

@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Oct 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

This branch has not been deployed

No deployments
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.

TUI: Locale.truncate splits emoji surrogates and under-truncates CJK — UTF-16 units instead of display width

1 participant