Skip to content

Commit ec5de22

Browse files
Mazyodclaude
andcommitted
feat: add requires_file_on_disk protocol for backend-specific file handling
Add LSPBackend.requires_file_on_disk() protocol method to allow backends to declare whether they need files written to disk for analysis. This fixes ty backend which cannot analyze virtual documents without corresponding files on disk. Changes: - Add requires_file_on_disk() to LSPBackend protocol - Implement in PyrightBackend (False), PyreflyBackend (False), TyBackend (True) - Session.create() now writes initial code to disk when required - Session.update_code() keeps disk files in sync - Update tests to use tmp_path/base_path to avoid stray files in project root - Remove xfails for ty tests that now pass (diagnostics, completion, rename, recycling) - Update ty KNOWN_LIMITATIONS.md to document automatic handling Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent bb4de8a commit ec5de22

7 files changed

Lines changed: 278 additions & 108 deletions

File tree

lsp_types/pyrefly/backend.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,7 @@ def get_workspace_settings(
8484
def get_semantic_tokens_legend(self) -> types.SemanticTokensLegend | None:
8585
"""Pyrefly doesn't advertise legend via LSP, return hardcoded legend."""
8686
return PYREFLY_LEGEND
87+
88+
def requires_file_on_disk(self) -> bool:
89+
"""Pyrefly supports virtual documents, no file needed on disk."""
90+
return False

lsp_types/pyright/backend.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,7 @@ def get_workspace_settings(
8383
def get_semantic_tokens_legend(self) -> types.SemanticTokensLegend | None:
8484
"""Pyright advertises legend via LSP, use server-provided."""
8585
return None
86+
87+
def requires_file_on_disk(self) -> bool:
88+
"""Pyright supports virtual documents, no file needed on disk."""
89+
return False

lsp_types/session.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ def get_semantic_tokens_legend(self) -> types.SemanticTokensLegend | None:
3636
"""Return hardcoded legend if server doesn't advertise one."""
3737
...
3838

39+
def requires_file_on_disk(self) -> bool:
40+
"""Return True if this backend requires files to exist on disk.
41+
42+
Some LSP backends (like ty) cannot analyze virtual documents opened via
43+
didOpen without a corresponding file on disk. For these backends, the
44+
session will write the initial code to disk before opening the document.
45+
"""
46+
...
47+
3948

4049
@dc.dataclass(kw_only=True)
4150
class DiagnosticsResult:
@@ -118,6 +127,11 @@ async def create_lsp_process():
118127
workspace_settings
119128
)
120129

130+
# Write file to disk if backend requires it (e.g., ty)
131+
if backend.requires_file_on_disk():
132+
session._file_path.write_text(initial_code)
133+
session._file_on_disk = True
134+
121135
# Simulate opening a document
122136
await session._open_document(initial_code)
123137

@@ -139,11 +153,13 @@ def __init__(
139153
):
140154
self._process = lsp_process
141155
self._backend = backend
142-
self._document_uri = f"file://{base_path / 'new.py'}"
156+
self._file_path = base_path / "new.py"
157+
self._document_uri = f"file://{self._file_path}"
143158
self._document_version = 1
144159
self._document_text = ""
145160
self._active_pool: LSPProcessPool | None = pool
146161
self._diag_result: DiagnosticsResult | None = None
162+
self._file_on_disk = False # Set to True if file was written for backends that require it
147163

148164
# Semantic tokens normalization
149165
self._backend_legend = legend
@@ -170,6 +186,10 @@ async def update_code(self, code: str) -> int:
170186
self._document_version += 1
171187
self._document_text = code
172188

189+
# Keep file on disk in sync if required by backend
190+
if self._file_on_disk:
191+
self._file_path.write_text(code)
192+
173193
document_version = self._document_version
174194
await self._process.notify.did_change_text_document(
175195
{

lsp_types/ty/KNOWN_LIMITATIONS.md

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
This document describes known limitations and behavioral differences when using the ty backend compared to other LSP backends (Pyright, Pyrefly).
44

5-
## 1. Files Must Exist on Disk
5+
## 1. Files Must Exist on Disk (Handled Automatically)
66

77
**Limitation**: ty requires Python files to exist on disk before it can provide diagnostics, completion, rename, and other analysis features.
88

@@ -11,23 +11,9 @@ This document describes known limitations and behavioral differences when using
1111
- Empty or limited completion results
1212
- Rename operations may fail
1313

14-
**Workaround**: Always write files to disk before creating a session:
15-
```python
16-
from pathlib import Path
14+
**Resolution**: The `TyBackend` implements `requires_file_on_disk() -> True`, which tells `Session.create()` to automatically write the initial code to disk before opening the document. The `update_code()` method also keeps the file in sync with code changes.
1715

18-
base_path = Path("/tmp/my_project")
19-
base_path.mkdir(exist_ok=True)
20-
21-
code = "x: int = 'not an int'"
22-
(base_path / "new.py").write_text(code)
23-
24-
session = await Session.create(
25-
TyBackend(),
26-
base_path=base_path,
27-
initial_code=code,
28-
)
29-
diagnostics = await session.get_diagnostics() # Now returns errors
30-
```
16+
This is handled transparently - no user action required.
3117

3218
## 2. `workspace/didChangeConfiguration` Not Supported
3319

@@ -82,6 +68,17 @@ WARN Your LSP client doesn't support file watching: You may see stale results wh
8268

8369
**Impact**: If files are modified outside the LSP session (e.g., by external tools), ty may not detect the changes until the session is recreated.
8470

71+
## 7. Completion Item Resolution Not Supported
72+
73+
**Limitation**: ty does not support the `completionItem/resolve` LSP request.
74+
75+
**Behavior**: Calling `resolve_completion()` will raise an error:
76+
```
77+
Unknown request: completionItem/resolve (-32601)
78+
```
79+
80+
**Impact**: Completion items won't have extended documentation or additional metadata that resolution typically provides. Basic completion works fine.
81+
8582
---
8683

8784
## Version Information

lsp_types/ty/backend.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,7 @@ def get_workspace_settings(
109109
def get_semantic_tokens_legend(self) -> types.SemanticTokensLegend | None:
110110
"""ty advertises legend via LSP, use server-provided."""
111111
return None
112+
113+
def requires_file_on_disk(self) -> bool:
114+
"""ty requires files to exist on disk for analysis."""
115+
return True

0 commit comments

Comments
 (0)