Skip to content

[Repo Assist] perf: use minimal TextEdit range to preserve cursor position during format#132

Draft
github-actions[bot] wants to merge 1 commit intomasterfrom
repo-assist/improve-minimal-textedits-2026-04-05-5062f3de2fe413e3
Draft

[Repo Assist] perf: use minimal TextEdit range to preserve cursor position during format#132
github-actions[bot] wants to merge 1 commit intomasterfrom
repo-assist/improve-minimal-textedits-2026-04-05-5062f3de2fe413e3

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions bot commented Apr 5, 2026

🤖 This is an automated draft PR from Repo Assist, an AI assistant.

Summary

Replace the full-document TextEdit with a minimal-range TextEdit that covers only the characters actually changed by phpcbf.

Problem

The current formatter builds a TextEdit that spans the entire document:

let range = new Range(new Position(0, 0), lastLine.range.end);
resolve([new vscode.TextEdit(range, text)]);

VS Code applies this by replacing the complete document content. As a side-effect, the cursor jumps to position 0 on every format — even if the user was editing code in the middle or bottom of the file and phpcbf only fixed a few lines near the top.

This is a minor but annoying UX issue that affects every user who formats on save.

Fix

Add diffRegion(originalText, formattedText) to lib/utils.js. It finds the first and last character offset where the two strings differ, in O(n) time with no external dependencies. The unchanged prefix and suffix are excluded.

minimalEdits() in extension.js uses diffRegion + document.positionAt() to build a single TextEdit covering only that changed region:

// Before (full document replacement, cursor always jumps to 0):
resolve([new vscode.TextEdit(range, text)]);

// After (minimal replacement, cursor preserved outside the changed region):
resolve(minimalEdits(originalText, text, document));

Example: if phpcbf fixes indentation on lines 1–5 of a 200-line file, and the user's cursor is at line 150, the cursor no longer moves.

Trade-offs

Single TextEdit Using one TextEdit keeps the implementation simple; a multi-edit Myers diff would give even smaller edits at higher complexity.
Line-ending sensitivity positionAt() uses the document's own line endings; phpcbf output is written to a temp file and read back, so line endings should match the document.
Degenerate case If the entire file changes, the region covers the whole document — same as before, no regression.

Test Status

All 15 unit tests pass (node --test test/unit.test.js):

  • 7 existing findFiles tests — unchanged
  • 8 new diffRegion tests covering: identical strings, single-char changes, changes at start/end, line insertions, line deletions, and minimality of the unchanged suffix

Syntax check: node --check extension.js — no errors

⚠️ Integration tests require a GUI environment and cannot run in CI.

Generated by Repo Assist ·

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@346204513ecfa08b81566450d7d599556807389f

Generated by Repo Assist ·

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@346204513ecfa08b81566450d7d599556807389f

…ormat

Replace the full-document TextEdit with a minimal-range TextEdit that
covers only the characters actually changed by phpcbf.  This keeps the
cursor in place when the edit is outside the area the user is working in,
avoiding the disruptive jump to position 0 that happens with a full-document
replacement.

Implementation:
- Add diffRegion() to lib/utils.js — finds the first and last differing
  character offsets between the original and formatted text.
- Add minimalEdits() to extension.js — uses diffRegion() to build a single
  TextEdit covering only the changed region, then hands it to VS Code.
- Update provideDocumentFormattingEdits to call minimalEdits() instead of
  constructing a Range(0,0, lastLine.end) TextEdit.

Tests:
- 8 new unit tests for diffRegion() covering: identical strings, single-char
  changes, changes at start/end, insertions, deletions, and minimality of
  the unchanged suffix.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants