microsoft / microsoft/vscode

Rename-provider WorkspaceEdit loses destination casing through asCanonicalUri on Windows

Open
#336,303 0 comments 0 reactions 1 assignee Claimed by @hediet View on GitHub
Dominant language
TypeScript
Stars
193k
Forks
42.4k
PR merge metrics
PR metrics pending

Description

Does this issue occur when all extensions are disabled?: Not tested without extensions; the reproduction uses the Python/Pylance rename provider. Debugger observations below isolate a transformation in VS Code after it receives the provider's edit.

- VS Code Version: 1.137.0
- OS Version: Windows 11 Enterprise, build 26200; filesystem case sensitivity disabled
- Extensions: Python 2026.4.0, Pylance 2026.3.1 for the original symptom reproduction; also traced with a Pylance development build
- Latest Insiders: not separately tested

Related user report: https://github.com/microsoft/pylance-release/issues/8201

## Summary

A file-rename destination returned by a rename provider has the requested lowercase spelling, but VS Code's workspace-edit revival calls `asCanonicalUri` on `newResource` and replaces it with a previously cached uppercase URI. This can collapse a case-only rename to a no-op or restore an old filename's casing after an intermediate rename. Text edits retain the requested lowercase module name, leaving imports and the actual directory entry inconsistent.

## Steps to reproduce

On Windows with Python and Pylance 2026.3.1 enabled:

1. Open a folder containing `scenarios/main.py`:

```python
class Something: ...

x = Something()
```

2. On `Something` in the class declaration, run **Move symbol to new file**. This creates `scenarios/Something.py` and changes the importer to `from scenarios.Something import Something`.
3. In that import, put the cursor on the module component `Something` (not the imported class) and press F2. Rename it to `something`. The direct case-only rename is a no-op in this build.
4. Without reloading the window, use F2 on the same module component to rename it to `_something`. Confirm the disk file is `_something.py` and the import uses `scenarios._something`.
5. Use F2 again to rename `_something` to `something`.
6. Inspect the actual filename with directory enumeration, not only the Explorer label.

Expected: `scenarios/something.py` on disk and `from scenarios.something import Something` in the importer.

Actual: the file is named `scenarios/Something.py`, while the import is lowercase. Pylance reports the import as unresolved.

The fresh move, failed direct rename, and intermediate rename are intentional: the failure depends on previously remembered URI casing. A fresh independent fixture can behave differently.

## Debugger evidence

The following values were observed while performing the editor actions in a real VS Code session. Paths are sanitized; these are payload excerpts, not a standalone extension reproduction.

For the final `_something` -> `something` rename, the server returned a text edit using `scenarios.something` and this resource operation:

```json
{
"kind": "rename",
"oldUri": "file:///c%3A/repro/scenarios/_something.py",
"newUri": "file:///c%3A/repro/scenarios/something.py"
}
```

The renderer callback registered by `$registerRenameSupport` received the lowercase destination from `$provideRenameEdits`. Stepping through workspace-edit revival showed:

1. Incoming `newResource`: `file:///c%3A/repro/scenarios/something.py`.
2. `asCanonicalUri` finds an existing URI under the case-insensitive comparison key whose path spelling is `Something.py`.
3. The returned canonical URI replaces `newResource` with `file:///c%3A/repro/scenarios/Something.py`.
4. The text edit remains lowercase. The resulting directory entry follows the altered uppercase destination.

A direct case-only probe also showed a valid `Something.py` -> `something.py` resource operation arriving in the renderer, then becoming identical uppercase old/new resource URIs after canonicalization.

This is not a request to make resource identity globally case-sensitive. A rename destination carries the spelling requested for the new directory entry, which should not be replaced with a previously remembered spelling during edit revival.

Pylance also had separate import-edit suppression problems for case-only renames. Those do not explain the final intermediate-rename failure above: both the text edit and resource destination were correct when they reached VS Code. Correcting the provider's direct-rename text edits likewise does not prevent the client from retaining uppercase disk spelling.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.