Skip to content

fix(opencode): use structuredPatch with 5s timeout to fix TUI freeze on large files - #20495

Closed
FindTea wants to merge 3 commits into
anomalyco:devfrom
FindTea:fix/large-file-perf
Closed

FindTea wants to merge 3 commits into
anomalyco:devfrom
FindTea:fix/large-file-perf

Conversation

@FindTea

@FindTea FindTea commented Apr 1, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #20477

Type of change

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

What does this PR do?

The write/edit/apply_patch tools call createTwoFilesPatch synchronously before showing the permission prompt. On large or heavily-modified content (e.g. a full file rewrite) the Myers diff algorithm can take many seconds, blocking the Node.js event loop the entire time and freezing the TUI on 'Preparing write...' / 'Preparing patch...'.

Fix: switch all three tools to structuredPatch (same underlying algorithm) with a hard 5-second timeout, matching the approach used in Claude Code's diff.ts. On timeout the diff preview is omitted but the permission prompt fires immediately and the file write is unaffected.

Also:

  • edit.ts: replace the post-write diffLines pass with a line-count over the already-computed diff string, avoiding a second O(N*D) traversal
  • apply_patch.ts: catch EEXIST from fs.mkdir on Windows (Bun 1.3.x bug where recursive:true still throws when the directory already exists)
  • bash.ts: throttle ctx.metadata() updates to at most once per 100ms to avoid flooding the event loop during high-volume command output

How did you verify your code works?

Ran bun test test/tool/ locally. All pre-existing passes still pass. Manually verified large file writes no longer hang indefinitely.

Screenshots / recordings

N/A - not a UI change

Checklist

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

FindTea added 3 commits April 7, 2026 09:30
- write/edit/apply_patch: skip createTwoFilesPatch and diffLines for
  files > 100 KB to avoid blocking the Node.js event loop. The Myers
  diff algorithm is O(N^2) and was the root cause of 'Preparing write...'
  / 'Preparing patch...' hanging for several seconds on large files.
  For oversized files, diff preview is omitted and additions/deletions
  are approximated by line count.

- edit: remove redundant Filesystem.readText + second diff recompute
  after write; use in-memory contentNew instead.

- apply_patch: catch EEXIST from fs.mkdir on Windows (Bun 1.3.x bug
  where recursive:true still throws when directory already exists).

- bash: throttle ctx.metadata() updates to at most once per 100 ms to
  prevent flooding the event loop during high-volume command output
  (e.g. compiler output, npm install). Final metadata is always flushed
  on process close.

- write.txt: hint AI to prefer Edit over Write for large files.
- edit.ts: restore Filesystem.readText after Format.file() so the stored
  contentNew reflects the formatter's output, not the pre-format content.
  Removing this was incorrect — the original intent was to capture the
  final on-disk state for accurate diff/snapshot tracking.

- edit.ts, write.ts: restore await on LSP.touchFile so diagnostics are
  fetched after the LSP has processed the file change, not before.
…woFilesPatch

createTwoFilesPatch has no timeout and runs Myers diff synchronously on the
main thread. On large or heavily-modified content (e.g. a full file rewrite)
this degrades to O(N*D) which blocks the Node.js event loop for several
seconds, causing the TUI to freeze on 'Preparing write...' / 'Preparing patch...'.

Switch all three file tools to structuredPatch (same underlying algorithm) with
a hard 5-second timeout, matching the approach used in Claude Code's diff.ts.
On timeout the diff preview is omitted but the permission prompt still fires
immediately -- the actual file write is unaffected.

Also replace the post-write diffLines pass in edit.ts with a simple line-count
over the already-computed diff string, avoiding a second O(N*D) traversal.

apply_patch: new-file hunks skip diff entirely (no previous content to compare).
apply_patch: EEXIST guard on fs.mkdir kept from previous commit (Bun 1.3.x bug).
@FindTea
FindTea force-pushed the fix/large-file-perf branch from 237e8e6 to d5a3c9f Compare April 7, 2026 01:31
@FindTea

FindTea commented Apr 7, 2026

Copy link
Copy Markdown
Author

Closing for now — the structuredPatch + imeout approach doesn't actually protect against the worst case (full-file rewrites) in synchronous mode, since the timeout is only checked between editLength iterations, not between individual comparisons. Will revisit with a more effective fix.

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.

write/edit freezes when writing long content ("Preparing write..." hangs)

1 participant