Skip to content

Commit 8afa2c5

Browse files
committed
update
1 parent be8446c commit 8afa2c5

File tree

11 files changed

+52
-353
lines changed

11 files changed

+52
-353
lines changed

schema/rename_execute.request.json

Lines changed: 0 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,6 @@
11
{
2-
"$defs": {
3-
"LineScope": {
4-
"description": "Scope by line range",
5-
"properties": {
6-
"line": {
7-
"anyOf": [
8-
{
9-
"type": "integer"
10-
},
11-
{
12-
"maxItems": 2,
13-
"minItems": 2,
14-
"prefixItems": [
15-
{
16-
"type": "integer"
17-
},
18-
{
19-
"type": "integer"
20-
}
21-
],
22-
"type": "array"
23-
}
24-
],
25-
"title": "Line"
26-
}
27-
},
28-
"required": [
29-
"line"
30-
],
31-
"title": "LineScope",
32-
"type": "object"
33-
},
34-
"Locate": {
35-
"description": "Two-stage location: scope \u2192 find.\n\nResolution rules:\n 1. SymbolScope without find: symbol declaration position (for references, rename)\n 2. With find containing marker: marker position\n 3. With find only: start of matched text\n 4. No scope + find: search entire file\n\nMarker Detection:\n The marker is automatically detected using nested bracket notation:\n - <|> (single level)\n - <<|>> (double level) if <|> appears more than once\n ... and so on\n\n The marker with the deepest nesting level that appears exactly once\n is chosen as the position marker.\n\nExamples:\n # Symbol declaration\n Locate(file_path=\"foo.py\", scope=SymbolScope(symbol_path=[\"MyClass\"]))\n\n # Completion trigger point - basic marker\n Locate(file_path=\"foo.py\", find=\"self.<|>\")\n\n # When <|> exists in source, use deeper nesting\n Locate(file_path=\"foo.py\", find=\"x = <|> + y <<|>> z\")\n # Will use <<|>> as the position marker\n\n # Specific location in function\n Locate(\n file_path=\"foo.py\",\n scope=SymbolScope(symbol_path=[\"process\"]),\n find=\"return <|>result\"\n )",
36-
"properties": {
37-
"file_path": {
38-
"format": "path",
39-
"title": "File Path",
40-
"type": "string"
41-
},
42-
"scope": {
43-
"anyOf": [
44-
{
45-
"$ref": "#/$defs/LineScope"
46-
},
47-
{
48-
"$ref": "#/$defs/SymbolScope"
49-
},
50-
{
51-
"type": "null"
52-
}
53-
],
54-
"default": null,
55-
"title": "Scope"
56-
},
57-
"find": {
58-
"anyOf": [
59-
{
60-
"type": "string"
61-
},
62-
{
63-
"type": "null"
64-
}
65-
],
66-
"default": null,
67-
"title": "Find"
68-
}
69-
},
70-
"required": [
71-
"file_path"
72-
],
73-
"title": "Locate",
74-
"type": "object"
75-
},
76-
"SymbolScope": {
77-
"description": "Scope by symbol, also serves as declaration locator when find is omitted",
78-
"properties": {
79-
"symbol_path": {
80-
"items": {
81-
"type": "string"
82-
},
83-
"title": "Symbol Path",
84-
"type": "array"
85-
}
86-
},
87-
"required": [
88-
"symbol_path"
89-
],
90-
"title": "SymbolScope",
91-
"type": "object"
92-
}
93-
},
942
"description": "Executes a rename operation, applying changes to the workspace.\n\nMust use a rename_id from a previous preview to ensure\nthe same changes are applied. Supports excluding specific files.",
953
"properties": {
96-
"locate": {
97-
"$ref": "#/$defs/Locate"
98-
},
99-
"new_name": {
100-
"description": "The new name for the symbol",
101-
"title": "New Name",
102-
"type": "string"
103-
},
104-
"mode": {
105-
"const": "execute",
106-
"default": "execute",
107-
"title": "Mode",
108-
"type": "string"
109-
},
1104
"rename_id": {
1115
"description": "Required ID from a previous preview to apply",
1126
"title": "Rename Id",
@@ -123,8 +17,6 @@
12317
}
12418
},
12519
"required": [
126-
"locate",
127-
"new_name",
12820
"rename_id"
12921
],
13022
"title": "RenameExecuteRequest",

schema/rename_execute.response.json

Lines changed: 1 addition & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,5 @@
11
{
22
"$defs": {
3-
"LineScope": {
4-
"description": "Scope by line range",
5-
"properties": {
6-
"line": {
7-
"anyOf": [
8-
{
9-
"type": "integer"
10-
},
11-
{
12-
"maxItems": 2,
13-
"minItems": 2,
14-
"prefixItems": [
15-
{
16-
"type": "integer"
17-
},
18-
{
19-
"type": "integer"
20-
}
21-
],
22-
"type": "array"
23-
}
24-
],
25-
"title": "Line"
26-
}
27-
},
28-
"required": [
29-
"line"
30-
],
31-
"title": "LineScope",
32-
"type": "object"
33-
},
34-
"Locate": {
35-
"description": "Two-stage location: scope \u2192 find.\n\nResolution rules:\n 1. SymbolScope without find: symbol declaration position (for references, rename)\n 2. With find containing marker: marker position\n 3. With find only: start of matched text\n 4. No scope + find: search entire file\n\nMarker Detection:\n The marker is automatically detected using nested bracket notation:\n - <|> (single level)\n - <<|>> (double level) if <|> appears more than once\n ... and so on\n\n The marker with the deepest nesting level that appears exactly once\n is chosen as the position marker.\n\nExamples:\n # Symbol declaration\n Locate(file_path=\"foo.py\", scope=SymbolScope(symbol_path=[\"MyClass\"]))\n\n # Completion trigger point - basic marker\n Locate(file_path=\"foo.py\", find=\"self.<|>\")\n\n # When <|> exists in source, use deeper nesting\n Locate(file_path=\"foo.py\", find=\"x = <|> + y <<|>> z\")\n # Will use <<|>> as the position marker\n\n # Specific location in function\n Locate(\n file_path=\"foo.py\",\n scope=SymbolScope(symbol_path=[\"process\"]),\n find=\"return <|>result\"\n )",
36-
"properties": {
37-
"file_path": {
38-
"format": "path",
39-
"title": "File Path",
40-
"type": "string"
41-
},
42-
"scope": {
43-
"anyOf": [
44-
{
45-
"$ref": "#/$defs/LineScope"
46-
},
47-
{
48-
"$ref": "#/$defs/SymbolScope"
49-
},
50-
{
51-
"type": "null"
52-
}
53-
],
54-
"default": null,
55-
"title": "Scope"
56-
},
57-
"find": {
58-
"anyOf": [
59-
{
60-
"type": "string"
61-
},
62-
{
63-
"type": "null"
64-
}
65-
],
66-
"default": null,
67-
"title": "Find"
68-
}
69-
},
70-
"required": [
71-
"file_path"
72-
],
73-
"title": "Locate",
74-
"type": "object"
75-
},
763
"RenameDiff": {
774
"description": "Line-level change showing before and after rename.",
785
"properties": {
@@ -104,20 +31,6 @@
10431
"RenameExecuteRequest": {
10532
"description": "Executes a rename operation, applying changes to the workspace.\n\nMust use a rename_id from a previous preview to ensure\nthe same changes are applied. Supports excluding specific files.",
10633
"properties": {
107-
"locate": {
108-
"$ref": "#/$defs/Locate"
109-
},
110-
"new_name": {
111-
"description": "The new name for the symbol",
112-
"title": "New Name",
113-
"type": "string"
114-
},
115-
"mode": {
116-
"const": "execute",
117-
"default": "execute",
118-
"title": "Mode",
119-
"type": "string"
120-
},
12134
"rename_id": {
12235
"description": "Required ID from a previous preview to apply",
12336
"title": "Rename Id",
@@ -134,8 +47,6 @@
13447
}
13548
},
13649
"required": [
137-
"locate",
138-
"new_name",
13950
"rename_id"
14051
],
14152
"title": "RenameExecuteRequest",
@@ -163,26 +74,9 @@
16374
],
16475
"title": "RenameFileChange",
16576
"type": "object"
166-
},
167-
"SymbolScope": {
168-
"description": "Scope by symbol, also serves as declaration locator when find is omitted",
169-
"properties": {
170-
"symbol_path": {
171-
"items": {
172-
"type": "string"
173-
},
174-
"title": "Symbol Path",
175-
"type": "array"
176-
}
177-
},
178-
"required": [
179-
"symbol_path"
180-
],
181-
"title": "SymbolScope",
182-
"type": "object"
18377
}
18478
},
185-
"markdown": "\n# Rename Applied: `{{ old_name }}` \u2192 `{{ new_name }}`\n\nSummary: Modified {{ total_files }} files with {{ total_occurrences }} occurrences.\n\n{% assign num_changes = changes | size -%}\n{% if num_changes == 0 -%}\nNo changes applied.\n{%- else -%}\n{%- for file in changes %}\n## `{{ file.file_path }}`\n{% for diff in file.diffs %}\nLine {{ diff.line }}:\n```diff\n- {{ diff.original }}\n+ {{ diff.modified }}\n```\n{% endfor %}\n{% endfor -%}\n---\n> [!NOTE]\n> Rename completed successfully.{% assign num_excluded = request.exclude_files | size %}{% if num_excluded > 0 %} Excluded files: {% for f in request.exclude_files %}`{{ f }}`{% unless forloop.last %}, {% endunless %}{% endfor %}{% endif %}\n{%- endif %}\n",
79+
"markdown": "\n# Rename Applied: `{{ old_name }}` \u2192 `{{ new_name }}`\n\nSummary: Modified {{ total_files }} files with {{ total_occurrences }} occurrences.\n\n{% assign num_changes = changes | size -%}\n{% if num_changes > 0 -%}\n{%- for file in changes %}\n- `{{ file.file_path }}`: {{ file.diffs | size }} occurrences\n{%- endfor %}\n{%- endif %}\n---\n> [!NOTE]\n> Rename completed successfully.{% assign num_excluded = request.exclude_files | size %}{% if num_excluded > 0 %} Excluded files: {% for f in request.exclude_files %}`{{ f }}`{% unless forloop.last %}, {% endunless %}{% endfor %}.\n> [!IMPORTANT]\n> You must manually rename the symbol in the excluded files to maintain consistency.{% endif %}\n",
18680
"properties": {
18781
"request": {
18882
"$ref": "#/$defs/RenameExecuteRequest"

schema/rename_preview.request.json

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,6 @@
100100
"description": "The new name for the symbol",
101101
"title": "New Name",
102102
"type": "string"
103-
},
104-
"mode": {
105-
"const": "preview",
106-
"default": "preview",
107-
"title": "Mode",
108-
"type": "string"
109103
}
110104
},
111105
"required": [

schema/rename_preview.response.json

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,6 @@
134134
"description": "The new name for the symbol",
135135
"title": "New Name",
136136
"type": "string"
137-
},
138-
"mode": {
139-
"const": "preview",
140-
"default": "preview",
141-
"title": "Mode",
142-
"type": "string"
143137
}
144138
},
145139
"required": [
@@ -167,7 +161,7 @@
167161
"type": "object"
168162
}
169163
},
170-
"markdown": "\n# Rename Preview: `{{ old_name }}` \u2192 `{{ new_name }}`\n\nID: `{{ rename_id }}`\nSummary: Affects {{ total_files }} files and {{ total_occurrences }} occurrences.\n\n{% assign num_changes = changes | size -%}\n{% if num_changes == 0 -%}\nNo changes to preview.\n{%- else -%}\n{%- for file in changes %}\n## `{{ file.file_path }}`\n{% for diff in file.diffs %}\nLine {{ diff.line }}:\n```diff\n- {{ diff.original }}\n+ {{ diff.modified }}\n```\n{% endfor %}\n{% endfor -%}\n---\n> [!TIP]\n> To apply this rename, use `mode=\"execute\"` with `rename_id=\"{{ rename_id }}\"`.\n> To exclude files, add `exclude_files=[\"path/to/exclude.py\"]`.\n{%- endif %}\n",
164+
"markdown": "\n# Rename Preview: `{{ old_name }}` \u2192 `{{ new_name }}`\n\nID: `{{ rename_id }}`\nSummary: Affects {{ total_files }} files and {{ total_occurrences }} occurrences.\n\n{% assign num_changes = changes | size -%}\n{% if num_changes == 0 -%}\nNo changes to preview.\n{%- else -%}\n{%- for file in changes %}\n## `{{ file.file_path }}`\n{% for diff in file.diffs %}\nLine {{ diff.line }}:\n```diff\n- {{ diff.original }}\n+ {{ diff.modified }}\n```\n{% endfor %}\n{% endfor -%}\n---\n> [!TIP]\n> To apply this rename, use `rename_execute` with `rename_id=\"{{ rename_id }}\"`.\n> To exclude files, add `exclude_files=[\"path/to/exclude.py\"]`.\n{%- endif %}\n",
171165
"properties": {
172166
"request": {
173167
"$ref": "#/$defs/RenamePreviewRequest"

src/lsap/capability/rename.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,14 @@ class RenameClient(
4141
): ...
4242

4343

44-
_preview_cache: LRUCache[str, lsp_type.WorkspaceEdit] = LRUCache()
44+
@define
45+
class CachedRename:
46+
edit: lsp_type.WorkspaceEdit
47+
old_name: str
48+
new_name: str
49+
50+
51+
_preview_cache: LRUCache[str, CachedRename] = LRUCache()
4552

4653

4754
def _get_old_name(
@@ -135,7 +142,9 @@ async def __call__(self, req: RenamePreviewRequest) -> RenamePreviewResponse | N
135142
return None
136143

137144
rid = generate_short_id()
138-
_preview_cache.put(rid, edit)
145+
_preview_cache.put(
146+
rid, CachedRename(edit=edit, old_name=old_name, new_name=req.new_name)
147+
)
139148
changes = await self._to_changes(edit, readers={path: reader})
140149

141150
return RenamePreviewResponse(
@@ -213,39 +222,30 @@ async def _to_file_change(
213222
class RenameExecuteCapability(
214223
Capability[RenameClient, RenameExecuteRequest, RenameExecuteResponse]
215224
):
216-
@cached_property
217-
def locate(self) -> LocateCapability:
218-
return LocateCapability(self.client)
219-
220225
@override
221226
async def __call__(self, req: RenameExecuteRequest) -> RenameExecuteResponse | None:
222-
edit = _preview_cache.get(req.rename_id)
223-
if not edit or not (locate := await self.locate(req)):
227+
cached = _preview_cache.get(req.rename_id)
228+
if not cached:
224229
return None
225230

226-
path, pos = locate.file_path, locate.position.to_lsp()
227-
content = await self.client.read_file(path)
228-
reader = DocumentReader(content)
229-
230-
prepare = await self.client.request_prepare_rename(path, pos)
231-
if not prepare:
232-
return None
231+
edit = cached.edit
232+
old_name = cached.old_name
233+
new_name = cached.new_name
233234

234-
old_name = _get_old_name(reader, pos, prepare)
235235
if req.exclude_files:
236236
edit = _filter_edit(self.client, edit, set(req.exclude_files))
237237

238238
total_occurrences = sum(
239239
len(edits) for _, edits in iter_text_document_edits(edit)
240240
)
241-
changes = await self._to_changes(edit, readers={path: reader})
241+
changes = await self._to_changes(edit)
242242
await self.client.apply_workspace_edit(edit)
243243
_preview_cache.pop(req.rename_id)
244244

245245
return RenameExecuteResponse(
246246
request=req,
247247
old_name=old_name,
248-
new_name=req.new_name,
248+
new_name=new_name,
249249
total_files=len(changes),
250250
total_occurrences=total_occurrences,
251251
changes=changes,

0 commit comments

Comments
 (0)