jaclang 0.8.0__py3-none-any.whl → 0.8.1__py3-none-any.whl

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.

Potentially problematic release.


This version of jaclang might be problematic. Click here for more details.

Files changed (77) hide show
  1. jaclang/cli/cli.py +11 -9
  2. jaclang/compiler/jac.lark +2 -12
  3. jaclang/compiler/larkparse/jac_parser.py +1 -1
  4. jaclang/compiler/parser.py +360 -521
  5. jaclang/compiler/passes/main/cfg_build_pass.py +2 -2
  6. jaclang/compiler/passes/main/def_impl_match_pass.py +14 -13
  7. jaclang/compiler/passes/main/def_use_pass.py +4 -7
  8. jaclang/compiler/passes/main/import_pass.py +3 -3
  9. jaclang/compiler/passes/main/inheritance_pass.py +2 -2
  10. jaclang/compiler/passes/main/pyast_gen_pass.py +196 -218
  11. jaclang/compiler/passes/main/pyast_load_pass.py +115 -311
  12. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +8 -7
  13. jaclang/compiler/passes/main/sym_tab_build_pass.py +3 -3
  14. jaclang/compiler/passes/main/sym_tab_link_pass.py +4 -4
  15. jaclang/compiler/passes/main/tests/fixtures/symtab_link_tests/action/actions.jac +1 -5
  16. jaclang/compiler/passes/main/tests/fixtures/symtab_link_tests/main.jac +1 -8
  17. jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +4 -2
  18. jaclang/compiler/passes/tool/doc_ir_gen_pass.py +197 -120
  19. jaclang/compiler/program.py +2 -7
  20. jaclang/compiler/tests/fixtures/fam.jac +2 -2
  21. jaclang/compiler/tests/fixtures/pkg_import_lib/__init__.jac +1 -0
  22. jaclang/compiler/tests/fixtures/pkg_import_lib/sub/__init__.jac +1 -0
  23. jaclang/compiler/tests/fixtures/pkg_import_lib/sub/helper.jac +3 -0
  24. jaclang/compiler/tests/fixtures/pkg_import_lib/tools.jac +3 -0
  25. jaclang/compiler/tests/fixtures/pkg_import_lib_py/__init__.py +11 -0
  26. jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/__init__.py +7 -0
  27. jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/helper.jac +3 -0
  28. jaclang/compiler/tests/fixtures/pkg_import_lib_py/tools.jac +3 -0
  29. jaclang/compiler/tests/fixtures/pkg_import_main.jac +10 -0
  30. jaclang/compiler/tests/fixtures/pkg_import_main_py.jac +11 -0
  31. jaclang/compiler/tests/test_importer.py +20 -0
  32. jaclang/compiler/tests/test_parser.py +1 -0
  33. jaclang/compiler/unitree.py +456 -304
  34. jaclang/langserve/engine.jac +498 -0
  35. jaclang/langserve/sem_manager.jac +309 -0
  36. jaclang/langserve/server.jac +186 -0
  37. jaclang/langserve/tests/server_test/test_lang_serve.py +6 -7
  38. jaclang/langserve/tests/server_test/utils.py +4 -1
  39. jaclang/langserve/tests/session.jac +294 -0
  40. jaclang/langserve/tests/test_sem_tokens.py +2 -2
  41. jaclang/langserve/tests/test_server.py +12 -7
  42. jaclang/langserve/utils.jac +51 -30
  43. jaclang/runtimelib/archetype.py +1 -1
  44. jaclang/runtimelib/builtin.py +17 -14
  45. jaclang/runtimelib/importer.py +26 -8
  46. jaclang/runtimelib/machine.py +96 -55
  47. jaclang/runtimelib/tests/fixtures/traversing_save.jac +7 -5
  48. jaclang/runtimelib/utils.py +3 -3
  49. jaclang/tests/fixtures/backward_edge_visit.jac +31 -0
  50. jaclang/tests/fixtures/builtin_printgraph.jac +85 -0
  51. jaclang/tests/fixtures/builtin_printgraph_json.jac +21 -0
  52. jaclang/tests/fixtures/builtin_printgraph_mermaid.jac +16 -0
  53. jaclang/tests/fixtures/chandra_bugs2.jac +20 -13
  54. jaclang/tests/fixtures/concurrency.jac +1 -1
  55. jaclang/tests/fixtures/edge_ability.jac +49 -0
  56. jaclang/tests/fixtures/guess_game.jac +1 -1
  57. jaclang/tests/fixtures/here_usage_error.jac +21 -0
  58. jaclang/tests/fixtures/here_visitor_usage.jac +21 -0
  59. jaclang/tests/fixtures/node_del.jac +30 -36
  60. jaclang/tests/fixtures/visit_traversal.jac +47 -0
  61. jaclang/tests/test_cli.py +12 -7
  62. jaclang/tests/test_language.py +91 -16
  63. jaclang/utils/helpers.py +14 -6
  64. jaclang/utils/lang_tools.py +2 -3
  65. jaclang/utils/tests/test_lang_tools.py +2 -1
  66. jaclang/utils/treeprinter.py +3 -4
  67. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/METADATA +4 -3
  68. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/RECORD +71 -55
  69. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/WHEEL +1 -1
  70. jaclang/langserve/engine.py +0 -553
  71. jaclang/langserve/sem_manager.py +0 -383
  72. jaclang/langserve/server.py +0 -167
  73. jaclang/langserve/tests/session.py +0 -255
  74. jaclang/tests/fixtures/builtin_dotgen.jac +0 -42
  75. jaclang/tests/fixtures/builtin_dotgen_json.jac +0 -21
  76. /jaclang/langserve/{__init__.py → __init__.jac} +0 -0
  77. {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/entry_points.txt +0 -0
@@ -1,553 +0,0 @@
1
- """Living Workspace of Jac project."""
2
-
3
- from __future__ import annotations
4
-
5
- import asyncio
6
- import logging
7
- import time
8
- from concurrent.futures import ThreadPoolExecutor
9
- from typing import Callable, Optional
10
-
11
- import jaclang.compiler.unitree as uni
12
- from jaclang import JacMachineInterface as Jac
13
- from jaclang.compiler.passes.main import CompilerMode as CMode
14
- from jaclang.compiler.program import JacProgram
15
- from jaclang.compiler.unitree import UniScopeNode
16
- from jaclang.langserve.sem_manager import SemTokManager
17
- from jaclang.vendor.pygls import uris
18
- from jaclang.vendor.pygls.server import LanguageServer
19
-
20
- import lsprotocol.types as lspt
21
-
22
- (utils,) = Jac.py_jac_import(".utils", base_path=__file__)
23
-
24
-
25
- class ModuleManager:
26
- """Handles Jac module, semantic manager, and alert management."""
27
-
28
- def __init__(self, program: JacProgram, sem_managers: dict) -> None:
29
- """Initialize ModuleManager."""
30
- self.program = program
31
- self.sem_managers = sem_managers
32
-
33
- def update(
34
- self, file_path: str, build: uni.Module, update_annexed: bool = True
35
- ) -> None:
36
- """Update modules in JacProgram's hub and semantic managers."""
37
- file_path = file_path.removeprefix("file://")
38
- self.program.mod.hub[file_path] = build
39
- self.sem_managers[file_path] = SemTokManager(ir=build)
40
- if update_annexed:
41
- for p, mod in self.program.mod.hub.items():
42
- if p != file_path:
43
- self.sem_managers[p] = SemTokManager(ir=mod)
44
-
45
- def clear_alerts_for_file(self, file_path_fs: str) -> None:
46
- """Remove errors and warnings for a specific file from the lists."""
47
- self.program.errors_had[:] = [
48
- e for e in self.program.errors_had if e.loc.mod_path != file_path_fs
49
- ]
50
- self.program.warnings_had[:] = [
51
- w for w in self.program.warnings_had if w.loc.mod_path != file_path_fs
52
- ]
53
-
54
-
55
- class JacLangServer(JacProgram, LanguageServer):
56
- """Jac Language Server, manages JacProgram and LSP."""
57
-
58
- def __init__(self) -> None:
59
- """Initialize JacLangServer."""
60
- LanguageServer.__init__(self, "jac-lsp", "v0.1")
61
- JacProgram.__init__(self)
62
- self.executor = ThreadPoolExecutor()
63
- self.tasks: dict[str, asyncio.Task] = {}
64
- self.sem_managers: dict[str, SemTokManager] = {}
65
- self.module_manager = ModuleManager(self, self.sem_managers)
66
-
67
- @property
68
- def diagnostics(self) -> dict[str, list]:
69
- """Return diagnostics for all files as a dict {uri: diagnostics}."""
70
- result = {}
71
- for file_path in self.mod.hub:
72
- uri = uris.from_fs_path(file_path)
73
- result[uri] = utils.gen_diagnostics(uri, self.errors_had, self.warnings_had)
74
- return result
75
-
76
- def _clear_alerts_for_file(self, file_path_fs: str) -> None:
77
- """Remove errors and warnings for a specific file from the lists."""
78
- self.module_manager.clear_alerts_for_file(file_path_fs)
79
-
80
- def get_ir(self, file_path: str) -> Optional[uni.Module]:
81
- """Get IR for a file path."""
82
- file_path = file_path.removeprefix("file://")
83
- return self.mod.hub.get(file_path)
84
-
85
- def update_modules(
86
- self, file_path: str, build: uni.Module, need: bool = True
87
- ) -> None:
88
- """Update modules in JacProgram's hub and semantic managers."""
89
- self.log_py(f"Updating modules for {file_path}")
90
- self.module_manager.update(file_path, build, update_annexed=need)
91
-
92
- def quick_check(self, file_path: str) -> bool:
93
- """Rebuild a file (syntax only)."""
94
- try:
95
- file_path_fs = file_path.removeprefix("file://")
96
- document = self.workspace.get_text_document(file_path)
97
- self._clear_alerts_for_file(file_path_fs)
98
- build = self.compile_from_str(
99
- source_str=document.source,
100
- file_path=document.path,
101
- mode=CMode.PARSE,
102
- )
103
- self.update_modules(file_path_fs, build, need=False)
104
- self.publish_diagnostics(
105
- file_path,
106
- utils.gen_diagnostics(file_path, self.errors_had, self.warnings_had),
107
- )
108
- return len(self.errors_had) == 0
109
- except Exception as e:
110
- self.log_error(f"Error during syntax check: {e}")
111
- return False
112
-
113
- def deep_check(self, file_path: str, annex_view: Optional[str] = None) -> bool:
114
- """Rebuild a file and its dependencies (typecheck)."""
115
- try:
116
- start_time = time.time()
117
- file_path_fs = file_path.removeprefix("file://")
118
- document = self.workspace.get_text_document(file_path)
119
- self._clear_alerts_for_file(file_path_fs)
120
- build = self.compile_from_str(
121
- source_str=document.source,
122
- file_path=document.path,
123
- mode=CMode.TYPECHECK,
124
- )
125
- self.update_modules(file_path_fs, build)
126
- if build.annexable_by:
127
- return self.deep_check(
128
- uris.from_fs_path(build.annexable_by), annex_view=file_path
129
- )
130
- self.publish_diagnostics(
131
- annex_view if annex_view else file_path,
132
- utils.gen_diagnostics(
133
- annex_view if annex_view else file_path,
134
- self.errors_had,
135
- self.warnings_had,
136
- ),
137
- )
138
- if annex_view:
139
- self.publish_diagnostics(
140
- file_path,
141
- utils.gen_diagnostics(
142
- file_path,
143
- self.errors_had,
144
- self.warnings_had,
145
- ),
146
- )
147
- self.log_py(f"PROFILE: Deep check took {time.time() - start_time} seconds.")
148
- return len(self.errors_had) == 0
149
- except Exception as e:
150
- self.log_error(f"Error during deep check: {e}")
151
- return False
152
-
153
- async def launch_quick_check(self, uri: str) -> None:
154
- """Analyze and publish diagnostics."""
155
- await asyncio.get_event_loop().run_in_executor(
156
- self.executor, self.quick_check, uri
157
- )
158
-
159
- async def launch_deep_check(self, uri: str) -> None:
160
- """Analyze and publish diagnostics."""
161
-
162
- async def run_in_executor(
163
- func: Callable[[str, Optional[str]], bool],
164
- file_path: str,
165
- annex_view: Optional[str] = None,
166
- ) -> None:
167
- loop = asyncio.get_event_loop()
168
- await loop.run_in_executor(self.executor, func, file_path, annex_view)
169
-
170
- if uri in self.tasks and not self.tasks[uri].done():
171
- self.log_py(f"Canceling {uri} deep check...")
172
- self.tasks[uri].cancel()
173
- del self.tasks[uri]
174
- self.log_py(f"Analyzing {uri}...")
175
- task = asyncio.create_task(run_in_executor(self.deep_check, uri))
176
- self.tasks[uri] = task
177
- await task
178
-
179
- def get_completion(
180
- self, file_path: str, position: lspt.Position, completion_trigger: Optional[str]
181
- ) -> lspt.CompletionList:
182
- """Return completion for a file."""
183
- document = self.workspace.get_text_document(file_path)
184
- mod_ir = self.get_ir(file_path)
185
- if not mod_ir:
186
- return lspt.CompletionList(is_incomplete=False, items=[])
187
- current_line = document.lines[position.line]
188
- current_pos = position.character
189
- current_symbol_path = utils.parse_symbol_path(current_line, current_pos)
190
- builtin_mod = next(
191
- mod for name, mod in self.mod.hub.items() if "builtins" in name
192
- )
193
- builtin_tab = builtin_mod.sym_tab
194
- assert isinstance(builtin_tab, UniScopeNode)
195
- completion_items = []
196
-
197
- node_selected = utils.find_deepest_symbol_node_at_pos(
198
- mod_ir,
199
- position.line,
200
- position.character - 2,
201
- )
202
- mod_tab = mod_ir.sym_tab if not node_selected else node_selected.sym_tab
203
- current_symbol_table = mod_tab
204
-
205
- if completion_trigger == ".":
206
- if current_symbol_path:
207
- temp_tab = mod_tab
208
- for symbol in current_symbol_path:
209
- if symbol == "self":
210
- is_ability_def = (
211
- temp_tab
212
- if isinstance(temp_tab, uni.ImplDef)
213
- else temp_tab.find_parent_of_type(uni.ImplDef)
214
- )
215
- if not is_ability_def:
216
- archi_owner = mod_tab.find_parent_of_type(uni.Archetype)
217
- temp_tab = (
218
- archi_owner.sym_tab
219
- if archi_owner and archi_owner.sym_tab
220
- else mod_tab
221
- )
222
- continue
223
- else:
224
- archi_owner = (
225
- (
226
- is_ability_def.decl_link.find_parent_of_type(
227
- uni.Archetype
228
- )
229
- )
230
- if is_ability_def.decl_link
231
- else None
232
- )
233
- temp_tab = (
234
- archi_owner.sym_tab
235
- if archi_owner and archi_owner.sym_tab
236
- else temp_tab
237
- )
238
- continue
239
- symb = temp_tab.lookup(symbol)
240
- if symb:
241
- fetc_tab = symb.fetch_sym_tab
242
- if fetc_tab:
243
- temp_tab = fetc_tab
244
- else:
245
- temp_tab = (
246
- symb.defn[0].type_sym_tab
247
- if symb.defn[0].type_sym_tab
248
- else temp_tab
249
- )
250
- else:
251
- break
252
- completion_items += utils.collect_all_symbols_in_scope(
253
- temp_tab, up_tree=False
254
- )
255
- if isinstance(temp_tab, uni.Archetype) and temp_tab.base_classes:
256
- base = []
257
- for base_name in temp_tab.base_classes.items:
258
- if isinstance(base_name, uni.Name) and base_name.sym:
259
- base.append(base_name.sym)
260
- for base_class_symbol in base:
261
- if base_class_symbol.fetch_sym_tab:
262
- completion_items += utils.collect_all_symbols_in_scope(
263
- base_class_symbol.fetch_sym_tab,
264
- up_tree=False,
265
- )
266
-
267
- else:
268
- if node_selected and (
269
- node_selected.find_parent_of_type(uni.Archetype)
270
- or node_selected.find_parent_of_type(uni.ImplDef)
271
- ):
272
- self_symbol = [
273
- lspt.CompletionItem(
274
- label="self", kind=lspt.CompletionItemKind.Variable
275
- )
276
- ]
277
- else:
278
- self_symbol = []
279
-
280
- completion_items += (
281
- utils.collect_all_symbols_in_scope(current_symbol_table)
282
- + self_symbol
283
- + utils.collect_child_tabs(builtin_tab)
284
- )
285
- return lspt.CompletionList(is_incomplete=False, items=completion_items)
286
-
287
- def rename_module(self, old_path: str, new_path: str) -> None:
288
- """Rename module."""
289
- if old_path in self.mod.hub and new_path != old_path:
290
- self.mod.hub[new_path] = self.mod.hub[old_path]
291
- self.sem_managers[new_path] = self.sem_managers[old_path]
292
- del self.mod.hub[old_path]
293
- del self.sem_managers[old_path]
294
-
295
- def delete_module(self, uri: str) -> None:
296
- """Delete module."""
297
- if uri in self.mod.hub:
298
- del self.mod.hub[uri]
299
- if uri in self.sem_managers:
300
- del self.sem_managers[uri]
301
-
302
- def formatted_jac(self, file_path: str) -> list[lspt.TextEdit]:
303
- """Return formatted jac."""
304
- try:
305
- document = self.workspace.get_text_document(file_path)
306
- formatted_text = JacProgram.jac_str_formatter(
307
- source_str=document.source, file_path=document.path
308
- )
309
- except Exception as e:
310
- self.log_error(f"Error during formatting: {e}")
311
- formatted_text = document.source
312
- return [
313
- lspt.TextEdit(
314
- range=lspt.Range(
315
- start=lspt.Position(line=0, character=0),
316
- end=lspt.Position(
317
- line=len(document.source.splitlines()) + 1, character=0
318
- ),
319
- ),
320
- new_text=(formatted_text),
321
- )
322
- ]
323
-
324
- def get_hover_info(
325
- self, file_path: str, position: lspt.Position
326
- ) -> Optional[lspt.Hover]:
327
- """Return hover information for a file."""
328
- file_path_fs = file_path.removeprefix("file://")
329
- if file_path_fs not in self.mod.hub:
330
- return None
331
- sem_mgr = self.sem_managers.get(file_path_fs)
332
- if not sem_mgr:
333
- return None
334
- token_index = utils.find_index(
335
- sem_mgr.sem_tokens,
336
- position.line,
337
- position.character,
338
- )
339
- if token_index is None:
340
- return None
341
- node_selected = sem_mgr.static_sem_tokens[token_index][3]
342
- value = self.get_node_info(node_selected) if node_selected else None
343
- if value:
344
- return lspt.Hover(
345
- contents=lspt.MarkupContent(
346
- kind=lspt.MarkupKind.PlainText, value=f"{value}"
347
- ),
348
- )
349
- return None
350
-
351
- def get_node_info(self, node: uni.AstSymbolNode) -> Optional[str]:
352
- """Extract meaningful information from the AST node."""
353
- try:
354
- if isinstance(node, uni.NameAtom):
355
- node = node.name_of
356
- access = node.sym.access.value + " " if node.sym else None
357
- node_info = (
358
- f"({access if access else ''}{node.sym_category.value}) {node.sym_name}"
359
- )
360
- if node.name_spec.clean_type:
361
- node_info += f": {node.name_spec.clean_type}"
362
- if isinstance(node, uni.AstDocNode) and node.doc:
363
- node_info += f"\n{node.doc.value}"
364
- if isinstance(node, uni.Ability) and node.signature:
365
- node_info += f"\n{node.signature.unparse()}"
366
- except AttributeError as e:
367
- self.log_warning(f"Attribute error when accessing node attributes: {e}")
368
- return node_info.strip()
369
-
370
- def get_outline(self, file_path: str) -> list[lspt.DocumentSymbol]:
371
- """Return document symbols for a file."""
372
- file_path_fs = file_path.removeprefix("file://")
373
- if file_path_fs in self.mod.hub and (
374
- root_node := self.mod.hub[file_path_fs].sym_tab
375
- ):
376
- return utils.get_symbols_for_outline(root_node)
377
- return []
378
-
379
- def get_definition(
380
- self, file_path: str, position: lspt.Position
381
- ) -> Optional[lspt.Location]:
382
- """Return definition location for a file."""
383
- file_path_fs = file_path.removeprefix("file://")
384
- if file_path_fs not in self.mod.hub:
385
- return None
386
- sem_mgr = self.sem_managers.get(file_path_fs)
387
- if not sem_mgr:
388
- return None
389
- token_index = utils.find_index(
390
- sem_mgr.sem_tokens,
391
- position.line,
392
- position.character,
393
- )
394
- if token_index is None:
395
- return None
396
- node_selected = sem_mgr.static_sem_tokens[token_index][3]
397
- if node_selected:
398
- if (
399
- isinstance(node_selected, uni.Name)
400
- and node_selected.parent
401
- and isinstance(node_selected.parent, uni.SubNodeList)
402
- and node_selected.parent.parent
403
- and isinstance(node_selected.parent.parent, uni.ModulePath)
404
- ):
405
- spec = node_selected.parent.parent.abs_path
406
- if spec:
407
- spec = spec[5:] if spec.startswith("File:") else spec
408
- return lspt.Location(
409
- uri=uris.from_fs_path(spec),
410
- range=lspt.Range(
411
- start=lspt.Position(line=0, character=0),
412
- end=lspt.Position(line=0, character=0),
413
- ),
414
- )
415
- else:
416
- return None
417
- elif node_selected.parent and isinstance(
418
- node_selected.parent, uni.ModuleItem
419
- ):
420
- path = (
421
- node_selected.parent.abs_path
422
- or node_selected.parent.from_mod_path.abs_path
423
- )
424
- loc_range = (0, 0, 0, 0)
425
-
426
- if path and loc_range:
427
- path = path[5:] if path.startswith("File:") else path
428
- return lspt.Location(
429
- uri=uris.from_fs_path(path),
430
- range=lspt.Range(
431
- start=lspt.Position(
432
- line=loc_range[0], character=loc_range[1]
433
- ),
434
- end=lspt.Position(
435
- line=loc_range[2], character=loc_range[3]
436
- ),
437
- ),
438
- )
439
- elif isinstance(node_selected, uni.ElementStmt):
440
- return None
441
- decl_node = (
442
- node_selected.parent.body.target
443
- if node_selected.parent
444
- and isinstance(node_selected.parent, uni.AstImplNeedingNode)
445
- and isinstance(node_selected.parent.body, uni.ImplDef)
446
- else (
447
- node_selected.sym.decl
448
- if (node_selected.sym and node_selected.sym.decl)
449
- else node_selected
450
- )
451
- )
452
- decl_uri = uris.from_fs_path(decl_node.loc.mod_path)
453
- try:
454
- decl_range = utils.create_range(decl_node.loc)
455
- except ValueError:
456
- return None
457
- decl_location = lspt.Location(
458
- uri=decl_uri,
459
- range=decl_range,
460
- )
461
-
462
- return decl_location
463
- else:
464
- return None
465
-
466
- def get_references(
467
- self, file_path: str, position: lspt.Position
468
- ) -> list[lspt.Location]:
469
- """Return references for a file."""
470
- file_path_fs = file_path.removeprefix("file://")
471
- if file_path_fs not in self.mod.hub:
472
- return []
473
- sem_mgr = self.sem_managers.get(file_path_fs)
474
- if not sem_mgr:
475
- return []
476
- index1 = utils.find_index(
477
- sem_mgr.sem_tokens,
478
- position.line,
479
- position.character,
480
- )
481
- if index1 is None:
482
- return []
483
- node_selected = sem_mgr.static_sem_tokens[index1][3]
484
- if node_selected and node_selected.sym:
485
- list_of_references: list[lspt.Location] = [
486
- lspt.Location(
487
- uri=uris.from_fs_path(node.loc.mod_path),
488
- range=utils.create_range(node.loc),
489
- )
490
- for node in node_selected.sym.uses
491
- ]
492
- return list_of_references
493
- return []
494
-
495
- def rename_symbol(
496
- self, file_path: str, position: lspt.Position, new_name: str
497
- ) -> Optional[lspt.WorkspaceEdit]:
498
- """Rename a symbol in a file."""
499
- file_path_fs = file_path.removeprefix("file://")
500
- if file_path_fs not in self.mod.hub:
501
- return None
502
- sem_mgr = self.sem_managers.get(file_path_fs)
503
- if not sem_mgr:
504
- return None
505
- index1 = utils.find_index(
506
- sem_mgr.sem_tokens,
507
- position.line,
508
- position.character,
509
- )
510
- if index1 is None:
511
- return None
512
- node_selected = sem_mgr.static_sem_tokens[index1][3]
513
- if node_selected and node_selected.sym:
514
- changes: dict[str, list[lspt.TextEdit]] = {}
515
- for node in [
516
- *node_selected.sym.uses,
517
- node_selected.sym.defn[0],
518
- ]:
519
- key = uris.from_fs_path(node.loc.mod_path)
520
- new_edit = lspt.TextEdit(
521
- range=utils.create_range(node.loc),
522
- new_text=new_name,
523
- )
524
- utils.add_unique_text_edit(changes, key, new_edit)
525
- return lspt.WorkspaceEdit(changes=changes)
526
- return None
527
-
528
- def get_semantic_tokens(self, file_path: str) -> lspt.SemanticTokens:
529
- """Return semantic tokens for a file."""
530
- file_path_fs = file_path.removeprefix("file://")
531
- sem_mgr = self.sem_managers.get(file_path_fs)
532
- if not sem_mgr:
533
- return lspt.SemanticTokens(data=[])
534
- return lspt.SemanticTokens(data=sem_mgr.sem_tokens)
535
-
536
- def log_error(self, message: str) -> None:
537
- """Log an error message."""
538
- self.show_message_log(message, lspt.MessageType.Error)
539
- self.show_message(message, lspt.MessageType.Error)
540
-
541
- def log_warning(self, message: str) -> None:
542
- """Log a warning message."""
543
- self.show_message_log(message, lspt.MessageType.Warning)
544
- self.show_message(message, lspt.MessageType.Warning)
545
-
546
- def log_info(self, message: str) -> None:
547
- """Log an info message."""
548
- self.show_message_log(message, lspt.MessageType.Info)
549
- self.show_message(message, lspt.MessageType.Info)
550
-
551
- def log_py(self, message: str) -> None:
552
- """Log a message."""
553
- logging.info(message)