@treedy/lsp-mcp 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/bundled/pyright/dist/index.d.ts +2 -0
  2. package/dist/bundled/pyright/dist/index.js +1620 -0
  3. package/dist/bundled/pyright/dist/index.js.map +26 -0
  4. package/dist/bundled/pyright/dist/lsp/connection.d.ts +71 -0
  5. package/dist/bundled/pyright/dist/lsp/document-manager.d.ts +67 -0
  6. package/dist/bundled/pyright/dist/lsp/index.d.ts +3 -0
  7. package/dist/bundled/pyright/dist/lsp/types.d.ts +55 -0
  8. package/dist/bundled/pyright/dist/lsp-client.d.ts +55 -0
  9. package/dist/bundled/pyright/dist/tools/completions.d.ts +18 -0
  10. package/dist/bundled/pyright/dist/tools/definition.d.ts +16 -0
  11. package/dist/bundled/pyright/dist/tools/diagnostics.d.ts +12 -0
  12. package/dist/bundled/pyright/dist/tools/hover.d.ts +16 -0
  13. package/dist/bundled/pyright/dist/tools/references.d.ts +16 -0
  14. package/dist/bundled/pyright/dist/tools/rename.d.ts +18 -0
  15. package/dist/bundled/pyright/dist/tools/search.d.ts +20 -0
  16. package/dist/bundled/pyright/dist/tools/signature-help.d.ts +16 -0
  17. package/dist/bundled/pyright/dist/tools/status.d.ts +14 -0
  18. package/dist/bundled/pyright/dist/tools/symbols.d.ts +17 -0
  19. package/dist/bundled/pyright/dist/tools/update-document.d.ts +14 -0
  20. package/dist/bundled/pyright/dist/utils/position.d.ts +33 -0
  21. package/dist/bundled/pyright/package.json +54 -0
  22. package/dist/bundled/python/README.md +230 -0
  23. package/dist/bundled/python/pyproject.toml +61 -0
  24. package/dist/bundled/python/src/rope_mcp/__init__.py +3 -0
  25. package/dist/bundled/python/src/rope_mcp/__pycache__/__init__.cpython-312.pyc +0 -0
  26. package/dist/bundled/python/src/rope_mcp/__pycache__/__init__.cpython-313.pyc +0 -0
  27. package/dist/bundled/python/src/rope_mcp/__pycache__/config.cpython-312.pyc +0 -0
  28. package/dist/bundled/python/src/rope_mcp/__pycache__/config.cpython-313.pyc +0 -0
  29. package/dist/bundled/python/src/rope_mcp/__pycache__/pyright_client.cpython-313.pyc +0 -0
  30. package/dist/bundled/python/src/rope_mcp/__pycache__/rope_client.cpython-313.pyc +0 -0
  31. package/dist/bundled/python/src/rope_mcp/__pycache__/server.cpython-312.pyc +0 -0
  32. package/dist/bundled/python/src/rope_mcp/__pycache__/server.cpython-313.pyc +0 -0
  33. package/dist/bundled/python/src/rope_mcp/config.py +408 -0
  34. package/dist/bundled/python/src/rope_mcp/lsp/__init__.py +15 -0
  35. package/dist/bundled/python/src/rope_mcp/lsp/__pycache__/__init__.cpython-313.pyc +0 -0
  36. package/dist/bundled/python/src/rope_mcp/lsp/__pycache__/client.cpython-313.pyc +0 -0
  37. package/dist/bundled/python/src/rope_mcp/lsp/__pycache__/types.cpython-313.pyc +0 -0
  38. package/dist/bundled/python/src/rope_mcp/lsp/client.py +624 -0
  39. package/dist/bundled/python/src/rope_mcp/lsp/types.py +82 -0
  40. package/dist/bundled/python/src/rope_mcp/pyright_client.py +147 -0
  41. package/dist/bundled/python/src/rope_mcp/rope_client.py +198 -0
  42. package/dist/bundled/python/src/rope_mcp/server.py +963 -0
  43. package/dist/bundled/python/src/rope_mcp/tools/__init__.py +26 -0
  44. package/dist/bundled/python/src/rope_mcp/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  45. package/dist/bundled/python/src/rope_mcp/tools/__pycache__/change_signature.cpython-313.pyc +0 -0
  46. package/dist/bundled/python/src/rope_mcp/tools/__pycache__/completions.cpython-313.pyc +0 -0
  47. package/dist/bundled/python/src/rope_mcp/tools/__pycache__/definition.cpython-313.pyc +0 -0
  48. package/dist/bundled/python/src/rope_mcp/tools/__pycache__/diagnostics.cpython-313.pyc +0 -0
  49. package/dist/bundled/python/src/rope_mcp/tools/__pycache__/hover.cpython-313.pyc +0 -0
  50. package/dist/bundled/python/src/rope_mcp/tools/__pycache__/move.cpython-313.pyc +0 -0
  51. package/dist/bundled/python/src/rope_mcp/tools/__pycache__/references.cpython-313.pyc +0 -0
  52. package/dist/bundled/python/src/rope_mcp/tools/__pycache__/rename.cpython-313.pyc +0 -0
  53. package/dist/bundled/python/src/rope_mcp/tools/__pycache__/search.cpython-313.pyc +0 -0
  54. package/dist/bundled/python/src/rope_mcp/tools/__pycache__/symbols.cpython-313.pyc +0 -0
  55. package/dist/bundled/python/src/rope_mcp/tools/change_signature.py +184 -0
  56. package/dist/bundled/python/src/rope_mcp/tools/completions.py +84 -0
  57. package/dist/bundled/python/src/rope_mcp/tools/definition.py +51 -0
  58. package/dist/bundled/python/src/rope_mcp/tools/diagnostics.py +18 -0
  59. package/dist/bundled/python/src/rope_mcp/tools/hover.py +49 -0
  60. package/dist/bundled/python/src/rope_mcp/tools/move.py +81 -0
  61. package/dist/bundled/python/src/rope_mcp/tools/references.py +60 -0
  62. package/dist/bundled/python/src/rope_mcp/tools/rename.py +61 -0
  63. package/dist/bundled/python/src/rope_mcp/tools/search.py +128 -0
  64. package/dist/bundled/python/src/rope_mcp/tools/symbols.py +118 -0
  65. package/dist/bundled/python/uv.lock +979 -0
  66. package/dist/bundled/typescript/dist/index.js +29534 -0
  67. package/dist/bundled/typescript/dist/index.js.map +211 -0
  68. package/dist/bundled/typescript/package.json +46 -0
  69. package/dist/bundled/vue/dist/index.d.ts +8 -0
  70. package/dist/bundled/vue/dist/index.js +21111 -0
  71. package/dist/bundled/vue/dist/ts-vue-service.d.ts +67 -0
  72. package/dist/bundled/vue/dist/vue-service.d.ts +144 -0
  73. package/dist/bundled/vue/package.json +45 -0
  74. package/dist/index.js +148 -58
  75. package/dist/index.js.map +4 -4
  76. package/package.json +1 -1
@@ -0,0 +1,26 @@
1
+ """Tool implementations for Rope MCP Server."""
2
+
3
+ from .hover import get_hover
4
+ from .definition import get_definition
5
+ from .references import get_references
6
+ from .completions import get_completions
7
+ from .symbols import get_symbols
8
+ from .rename import do_rename
9
+ from .move import do_move
10
+ from .change_signature import do_change_signature, get_function_signature
11
+ from .diagnostics import get_diagnostics
12
+ from .search import get_search
13
+
14
+ __all__ = [
15
+ "get_hover",
16
+ "get_definition",
17
+ "get_references",
18
+ "get_completions",
19
+ "get_symbols",
20
+ "do_rename",
21
+ "do_move",
22
+ "do_change_signature",
23
+ "get_function_signature",
24
+ "get_diagnostics",
25
+ "get_search",
26
+ ]
@@ -0,0 +1,184 @@
1
+ """Change signature tool implementation - modify function parameters."""
2
+
3
+ from typing import Optional
4
+
5
+ from rope.refactor.change_signature import ChangeSignature, ArgumentNormalizer
6
+
7
+ from ..rope_client import get_client
8
+ from ..lsp import refresh_lsp_documents
9
+
10
+
11
+ def do_change_signature(
12
+ file: str,
13
+ line: int,
14
+ column: int,
15
+ new_params: Optional[list[str]] = None,
16
+ add_param: Optional[dict] = None,
17
+ remove_param: Optional[str] = None,
18
+ resources_only: bool = False,
19
+ ) -> dict:
20
+ """Change the signature of a function.
21
+
22
+ Args:
23
+ file: Absolute path to the Python file
24
+ line: 1-based line number of the function
25
+ column: 1-based column number of the function
26
+ new_params: Complete new parameter list (reorder/rename), e.g. ["self", "b", "a"]
27
+ add_param: Add a parameter, e.g. {"name": "new_param", "default": "None", "index": 1}
28
+ remove_param: Name of parameter to remove
29
+ resources_only: If True, only return what would change without applying
30
+
31
+ Returns:
32
+ Dict containing the changes made or error message
33
+
34
+ Examples:
35
+ # Reorder parameters: def foo(a, b) -> def foo(b, a)
36
+ change_signature(file, line, col, new_params=["self", "b", "a"])
37
+
38
+ # Add parameter: def foo(a) -> def foo(a, b=None)
39
+ change_signature(file, line, col, add_param={"name": "b", "default": "None"})
40
+
41
+ # Remove parameter: def foo(a, b) -> def foo(a)
42
+ change_signature(file, line, col, remove_param="b")
43
+ """
44
+ client = get_client()
45
+
46
+ try:
47
+ workspace = client.find_workspace_for_file(file)
48
+ project = client.get_project(workspace)
49
+ resource = client.get_resource(project, file)
50
+ source = resource.read()
51
+ offset = client.position_to_offset(source, line, column)
52
+
53
+ # Create change signature refactoring
54
+ changer = ChangeSignature(project, resource, offset)
55
+
56
+ # Get current signature info
57
+ signature = changer.get_args()
58
+ original_params = [arg[0] for arg in signature] # Extract param names
59
+
60
+ # Build the changers list
61
+ changers = []
62
+
63
+ if new_params is not None:
64
+ # Reorder/rename parameters
65
+ # ArgumentNormalizer reorders arguments to match new order
66
+ changers.append(ArgumentNormalizer(new_params)) # type: ignore[call-arg]
67
+
68
+ if add_param is not None:
69
+ # Add a new parameter
70
+ from rope.refactor.change_signature import ArgumentAdder
71
+
72
+ name = add_param.get("name")
73
+ default = add_param.get("default")
74
+ index = add_param.get("index")
75
+ # If index is None, append at the end
76
+ if index is None:
77
+ index = len(original_params)
78
+ changers.append(ArgumentAdder(index, name, default))
79
+
80
+ if remove_param is not None:
81
+ # Remove a parameter
82
+ from rope.refactor.change_signature import ArgumentRemover
83
+
84
+ # Find the index of the parameter to remove
85
+ try:
86
+ param_index = original_params.index(remove_param)
87
+ changers.append(ArgumentRemover(param_index))
88
+ except ValueError:
89
+ return {
90
+ "error": f"Parameter '{remove_param}' not found in function signature",
91
+ "current_params": original_params,
92
+ }
93
+
94
+ if not changers:
95
+ return {
96
+ "error": "No changes specified. Use new_params, add_param, or remove_param.",
97
+ "current_params": original_params,
98
+ }
99
+
100
+ # Get the changes
101
+ changes = changer.get_changes(changers)
102
+
103
+ # Collect the changes to report
104
+ changed_files = []
105
+ for change in changes.get_changed_resources():
106
+ changed_files.append(change.real_path)
107
+
108
+ if resources_only:
109
+ return {
110
+ "preview": True,
111
+ "original_params": original_params,
112
+ "changed_files": changed_files,
113
+ "changes_count": len(changed_files),
114
+ }
115
+
116
+ # Apply the changes
117
+ project.do(changes)
118
+
119
+ # Refresh LSP documents so Pyright picks up the changes
120
+ refresh_lsp_documents(changed_files)
121
+
122
+ return {
123
+ "success": True,
124
+ "original_params": original_params,
125
+ "changed_files": changed_files,
126
+ "changes_count": len(changed_files),
127
+ }
128
+
129
+ except Exception as e:
130
+ return {
131
+ "error": str(e),
132
+ "file": file,
133
+ "line": line,
134
+ "column": column,
135
+ }
136
+
137
+
138
+ def get_function_signature(file: str, line: int, column: int) -> dict:
139
+ """Get the current signature of a function.
140
+
141
+ Args:
142
+ file: Absolute path to the Python file
143
+ line: 1-based line number of the function
144
+ column: 1-based column number of the function
145
+
146
+ Returns:
147
+ Dict containing the function signature info
148
+ """
149
+ client = get_client()
150
+
151
+ try:
152
+ workspace = client.find_workspace_for_file(file)
153
+ project = client.get_project(workspace)
154
+ resource = client.get_resource(project, file)
155
+ source = resource.read()
156
+ offset = client.position_to_offset(source, line, column)
157
+
158
+ # Create change signature refactoring to inspect signature
159
+ changer = ChangeSignature(project, resource, offset)
160
+
161
+ # Get current signature info
162
+ # Returns list of tuples: [(name, default_value), ...]
163
+ signature = changer.get_args()
164
+
165
+ params = []
166
+ for arg in signature:
167
+ param_info = {"name": arg[0]}
168
+ if len(arg) > 1 and arg[1] is not None:
169
+ param_info["default"] = arg[1]
170
+ params.append(param_info)
171
+
172
+ return {
173
+ "params": params,
174
+ "param_names": [p["name"] for p in params],
175
+ "count": len(params),
176
+ }
177
+
178
+ except Exception as e:
179
+ return {
180
+ "error": str(e),
181
+ "file": file,
182
+ "line": line,
183
+ "column": column,
184
+ }
@@ -0,0 +1,84 @@
1
+ """Completions tool implementation - code completion."""
2
+
3
+ from rope.contrib import codeassist
4
+
5
+ from ..rope_client import get_client
6
+
7
+
8
+ def get_completions(file: str, line: int, column: int) -> dict:
9
+ """Get code completion suggestions at the given position.
10
+
11
+ Args:
12
+ file: Absolute path to the Python file
13
+ line: 1-based line number
14
+ column: 1-based column number
15
+
16
+ Returns:
17
+ Dict containing completion items or error message
18
+ """
19
+ client = get_client()
20
+
21
+ try:
22
+ workspace = client.find_workspace_for_file(file)
23
+ project = client.get_project(workspace)
24
+ resource = client.get_resource(project, file)
25
+ source = resource.read()
26
+ offset = client.position_to_offset(source, line, column)
27
+
28
+ proposals = codeassist.code_assist(project, source, offset, resource)
29
+
30
+ # Sort proposals by relevance
31
+ proposals = codeassist.sorted_proposals(proposals)
32
+
33
+ completions = []
34
+ for proposal in proposals:
35
+ # Use scope instead of deprecated kind property
36
+ scope = getattr(proposal, "scope", None) or getattr(proposal, "kind", "")
37
+ item = {
38
+ "label": proposal.name,
39
+ "kind": _get_completion_kind(scope),
40
+ "detail": proposal.type or "",
41
+ }
42
+
43
+ # Add documentation if available
44
+ if hasattr(proposal, "get_doc") and callable(proposal.get_doc):
45
+ try:
46
+ doc = proposal.get_doc()
47
+ if doc:
48
+ item["documentation"] = doc
49
+ except Exception:
50
+ pass
51
+
52
+ completions.append(item)
53
+
54
+ return {
55
+ "completions": completions,
56
+ "count": len(completions),
57
+ }
58
+
59
+ except Exception as e:
60
+ return {
61
+ "error": str(e),
62
+ "file": file,
63
+ "line": line,
64
+ "column": column,
65
+ }
66
+
67
+
68
+ def _get_completion_kind(rope_kind: str) -> str:
69
+ """Map Rope completion kinds to standard kinds."""
70
+ kind_map = {
71
+ "function": "Function",
72
+ "class": "Class",
73
+ "module": "Module",
74
+ "variable": "Variable",
75
+ "attribute": "Property",
76
+ "builtin": "Constant",
77
+ "parameter": "Variable",
78
+ "keyword": "Keyword",
79
+ "imported": "Module",
80
+ "instance": "Variable",
81
+ "local": "Variable",
82
+ "global": "Variable",
83
+ }
84
+ return kind_map.get(rope_kind, "Text")
@@ -0,0 +1,51 @@
1
+ """Definition tool implementation - go to definition."""
2
+
3
+ from rope.contrib import codeassist
4
+
5
+ from ..rope_client import get_client
6
+
7
+
8
+ def get_definition(file: str, line: int, column: int) -> dict:
9
+ """Get the definition location for the symbol at the given position.
10
+
11
+ Args:
12
+ file: Absolute path to the Python file
13
+ line: 1-based line number
14
+ column: 1-based column number
15
+
16
+ Returns:
17
+ Dict containing definition location or error message
18
+ """
19
+ client = get_client()
20
+
21
+ try:
22
+ workspace = client.find_workspace_for_file(file)
23
+ project = client.get_project(workspace)
24
+ resource = client.get_resource(project, file)
25
+ source = resource.read()
26
+ offset = client.position_to_offset(source, line, column)
27
+
28
+ location = codeassist.get_definition_location(project, source, offset, resource)
29
+
30
+ if location and location[0] is not None:
31
+ def_resource, def_line = location
32
+ def_path = def_resource.real_path if def_resource else file
33
+
34
+ return {
35
+ "file": def_path,
36
+ "line": def_line or 1,
37
+ "column": 1, # Rope doesn't provide column info
38
+ }
39
+ else:
40
+ return {
41
+ "file": None,
42
+ "message": "No definition found at this position",
43
+ }
44
+
45
+ except Exception as e:
46
+ return {
47
+ "error": str(e),
48
+ "file": file,
49
+ "line": line,
50
+ "column": column,
51
+ }
@@ -0,0 +1,18 @@
1
+ """Diagnostics tool implementation using Pyright."""
2
+
3
+ from ..pyright_client import get_pyright_client
4
+
5
+
6
+ def get_diagnostics(path: str) -> dict:
7
+ """Get diagnostics (type errors, warnings) for a file or directory.
8
+
9
+ This tool uses Pyright for type checking and analysis.
10
+
11
+ Args:
12
+ path: Absolute path to a Python file or directory
13
+
14
+ Returns:
15
+ Dict containing diagnostics or error message
16
+ """
17
+ client = get_pyright_client()
18
+ return client.get_diagnostics(path)
@@ -0,0 +1,49 @@
1
+ """Hover tool implementation - get documentation at position."""
2
+
3
+ from rope.contrib import codeassist
4
+
5
+ from ..rope_client import get_client
6
+
7
+
8
+ def get_hover(file: str, line: int, column: int) -> dict:
9
+ """Get documentation for the symbol at the given position.
10
+
11
+ Args:
12
+ file: Absolute path to the Python file
13
+ line: 1-based line number
14
+ column: 1-based column number
15
+
16
+ Returns:
17
+ Dict containing documentation or error message
18
+ """
19
+ client = get_client()
20
+
21
+ try:
22
+ workspace = client.find_workspace_for_file(file)
23
+ project = client.get_project(workspace)
24
+ resource = client.get_resource(project, file)
25
+ source = resource.read()
26
+ offset = client.position_to_offset(source, line, column)
27
+
28
+ doc = codeassist.get_doc(project, source, offset, resource)
29
+
30
+ if doc:
31
+ return {
32
+ "contents": doc,
33
+ "file": file,
34
+ "line": line,
35
+ "column": column,
36
+ }
37
+ else:
38
+ return {
39
+ "contents": None,
40
+ "message": "No documentation available at this position",
41
+ }
42
+
43
+ except Exception as e:
44
+ return {
45
+ "error": str(e),
46
+ "file": file,
47
+ "line": line,
48
+ "column": column,
49
+ }
@@ -0,0 +1,81 @@
1
+ """Move tool implementation - move function/class to another module."""
2
+
3
+ from rope.refactor.move import MoveGlobal
4
+
5
+ from ..rope_client import get_client
6
+ from ..lsp import refresh_lsp_documents
7
+
8
+
9
+ def do_move(
10
+ file: str, line: int, column: int, destination: str, resources_only: bool = False
11
+ ) -> dict:
12
+ """Move a function or class to another module.
13
+
14
+ Args:
15
+ file: Absolute path to the Python file containing the symbol
16
+ line: 1-based line number of the symbol
17
+ column: 1-based column number of the symbol
18
+ destination: Destination module path (e.g., "mypackage.utils" or "utils.py")
19
+ resources_only: If True, only return what would change without applying
20
+
21
+ Returns:
22
+ Dict containing the changes made or error message
23
+ """
24
+ client = get_client()
25
+
26
+ try:
27
+ workspace = client.find_workspace_for_file(file)
28
+ project = client.get_project(workspace)
29
+ resource = client.get_resource(project, file)
30
+ source = resource.read()
31
+ offset = client.position_to_offset(source, line, column)
32
+
33
+ # Create move refactoring
34
+ mover = MoveGlobal(project, resource, offset)
35
+
36
+ # Get the destination resource
37
+ # Handle both module path (foo.bar) and file path (foo/bar.py)
38
+ if destination.endswith(".py"):
39
+ dest_resource = project.get_resource(destination)
40
+ else:
41
+ # Convert module path to file path
42
+ dest_path = destination.replace(".", "/") + ".py"
43
+ dest_resource = project.get_resource(dest_path)
44
+
45
+ # Get the changes
46
+ changes = mover.get_changes(dest_resource)
47
+
48
+ # Collect the changes to report
49
+ changed_files = []
50
+ for change in changes.get_changed_resources():
51
+ changed_files.append(change.real_path)
52
+
53
+ if resources_only:
54
+ return {
55
+ "preview": True,
56
+ "destination": destination,
57
+ "changed_files": changed_files,
58
+ "changes_count": len(changed_files),
59
+ }
60
+
61
+ # Apply the changes
62
+ project.do(changes)
63
+
64
+ # Refresh LSP documents so Pyright picks up the changes
65
+ refresh_lsp_documents(changed_files)
66
+
67
+ return {
68
+ "success": True,
69
+ "destination": destination,
70
+ "changed_files": changed_files,
71
+ "changes_count": len(changed_files),
72
+ }
73
+
74
+ except Exception as e:
75
+ return {
76
+ "error": str(e),
77
+ "file": file,
78
+ "line": line,
79
+ "column": column,
80
+ "destination": destination,
81
+ }
@@ -0,0 +1,60 @@
1
+ """References tool implementation - find all references."""
2
+
3
+ from rope.contrib import findit
4
+
5
+ from ..rope_client import get_client
6
+
7
+
8
+ def get_references(file: str, line: int, column: int) -> dict:
9
+ """Find all references to the symbol at the given position.
10
+
11
+ Args:
12
+ file: Absolute path to the Python file
13
+ line: 1-based line number
14
+ column: 1-based column number
15
+
16
+ Returns:
17
+ Dict containing list of references or error message
18
+ """
19
+ client = get_client()
20
+
21
+ try:
22
+ workspace = client.find_workspace_for_file(file)
23
+ project = client.get_project(workspace)
24
+ resource = client.get_resource(project, file)
25
+ source = resource.read()
26
+ offset = client.position_to_offset(source, line, column)
27
+
28
+ occurrences = findit.find_occurrences(
29
+ project, resource, offset, unsure=False, in_hierarchy=True
30
+ )
31
+
32
+ references = []
33
+ for occurrence in occurrences:
34
+ occ_resource = occurrence.resource
35
+ occ_offset = occurrence.offset
36
+
37
+ # Read source to convert offset to line/column
38
+ occ_source = occ_resource.read()
39
+ occ_line, occ_column = client.offset_to_position(occ_source, occ_offset)
40
+
41
+ references.append(
42
+ {
43
+ "file": occ_resource.real_path,
44
+ "line": occ_line,
45
+ "column": occ_column,
46
+ }
47
+ )
48
+
49
+ return {
50
+ "references": references,
51
+ "count": len(references),
52
+ }
53
+
54
+ except Exception as e:
55
+ return {
56
+ "error": str(e),
57
+ "file": file,
58
+ "line": line,
59
+ "column": column,
60
+ }
@@ -0,0 +1,61 @@
1
+ """Rename tool implementation - rename refactoring."""
2
+
3
+ from rope.refactor.rename import Rename
4
+
5
+ from ..rope_client import get_client
6
+ from ..lsp import refresh_lsp_documents
7
+
8
+
9
+ def do_rename(file: str, line: int, column: int, new_name: str) -> dict:
10
+ """Rename the symbol at the given position.
11
+
12
+ Args:
13
+ file: Absolute path to the Python file
14
+ line: 1-based line number
15
+ column: 1-based column number
16
+ new_name: The new name for the symbol
17
+
18
+ Returns:
19
+ Dict containing the changes made or error message
20
+ """
21
+ client = get_client()
22
+
23
+ try:
24
+ workspace = client.find_workspace_for_file(file)
25
+ project = client.get_project(workspace)
26
+ resource = client.get_resource(project, file)
27
+ source = resource.read()
28
+ offset = client.position_to_offset(source, line, column)
29
+
30
+ # Create rename refactoring
31
+ renamer = Rename(project, resource, offset)
32
+
33
+ # Get the changes
34
+ changes = renamer.get_changes(new_name)
35
+
36
+ # Collect the changes to report
37
+ changed_files = []
38
+ for change in changes.get_changed_resources():
39
+ changed_files.append(change.real_path)
40
+
41
+ # Apply the changes
42
+ project.do(changes)
43
+
44
+ # Refresh LSP documents so Pyright picks up the changes
45
+ refresh_lsp_documents(changed_files)
46
+
47
+ return {
48
+ "success": True,
49
+ "new_name": new_name,
50
+ "changed_files": changed_files,
51
+ "changes_count": len(changed_files),
52
+ }
53
+
54
+ except Exception as e:
55
+ return {
56
+ "error": str(e),
57
+ "file": file,
58
+ "line": line,
59
+ "column": column,
60
+ "new_name": new_name,
61
+ }