jaclang 0.7.14__py3-none-any.whl → 0.7.17__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 (131) hide show
  1. jaclang/cli/cli.py +147 -77
  2. jaclang/cli/cmdreg.py +9 -12
  3. jaclang/compiler/__init__.py +19 -53
  4. jaclang/compiler/absyntree.py +94 -16
  5. jaclang/compiler/constant.py +8 -8
  6. jaclang/compiler/jac.lark +4 -3
  7. jaclang/compiler/parser.py +41 -25
  8. jaclang/compiler/passes/ir_pass.py +4 -13
  9. jaclang/compiler/passes/main/__init__.py +1 -1
  10. jaclang/compiler/passes/main/access_modifier_pass.py +96 -147
  11. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +155 -54
  12. jaclang/compiler/passes/main/import_pass.py +99 -75
  13. jaclang/compiler/passes/main/py_collect_dep_pass.py +70 -0
  14. jaclang/compiler/passes/main/pyast_gen_pass.py +328 -565
  15. jaclang/compiler/passes/main/pyast_load_pass.py +33 -6
  16. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +7 -0
  17. jaclang/compiler/passes/main/registry_pass.py +37 -3
  18. jaclang/compiler/passes/main/schedules.py +9 -2
  19. jaclang/compiler/passes/main/sym_tab_build_pass.py +10 -6
  20. jaclang/compiler/passes/main/tests/__init__.py +1 -1
  21. jaclang/compiler/passes/main/tests/fixtures/autoimpl.empty.impl.jac +0 -0
  22. jaclang/compiler/passes/main/tests/fixtures/autoimpl.jac +1 -1
  23. jaclang/compiler/passes/main/tests/fixtures/py_imp_test.jac +29 -0
  24. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/__init__.py +3 -0
  25. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/color.py +3 -0
  26. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/constants.py +5 -0
  27. jaclang/compiler/passes/main/tests/fixtures/pygame_mock/display.py +2 -0
  28. jaclang/compiler/passes/main/tests/test_import_pass.py +72 -13
  29. jaclang/compiler/passes/main/type_check_pass.py +22 -5
  30. jaclang/compiler/passes/tool/jac_formatter_pass.py +135 -89
  31. jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +37 -41
  32. jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +37 -42
  33. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/access_mod_check.jac +27 -0
  34. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/architype_test.jac +13 -0
  35. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comment_alignment.jac +11 -0
  36. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comments.jac +13 -0
  37. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/decorator_stack.jac +37 -0
  38. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/esc_keywords.jac +5 -0
  39. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/long_names.jac +19 -0
  40. jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +6 -0
  41. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +11 -0
  42. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +33 -39
  43. jaclang/compiler/passes/transform.py +4 -0
  44. jaclang/compiler/passes/utils/mypy_ast_build.py +45 -0
  45. jaclang/compiler/semtable.py +31 -7
  46. jaclang/compiler/symtable.py +16 -11
  47. jaclang/compiler/tests/test_importer.py +25 -10
  48. jaclang/langserve/engine.py +104 -118
  49. jaclang/langserve/sem_manager.py +379 -0
  50. jaclang/langserve/server.py +24 -11
  51. jaclang/langserve/tests/fixtures/base_module_structure.jac +27 -6
  52. jaclang/langserve/tests/fixtures/circle.jac +3 -3
  53. jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
  54. jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
  55. jaclang/langserve/tests/fixtures/import_include_statements.jac +1 -1
  56. jaclang/langserve/tests/fixtures/rename.jac +30 -0
  57. jaclang/langserve/tests/test_sem_tokens.py +277 -0
  58. jaclang/langserve/tests/test_server.py +287 -17
  59. jaclang/langserve/utils.py +184 -98
  60. jaclang/plugin/builtin.py +1 -1
  61. jaclang/plugin/default.py +288 -92
  62. jaclang/plugin/feature.py +65 -27
  63. jaclang/plugin/spec.py +62 -23
  64. jaclang/plugin/tests/fixtures/other_root_access.jac +82 -0
  65. jaclang/plugin/tests/test_jaseci.py +414 -42
  66. jaclang/runtimelib/architype.py +650 -0
  67. jaclang/{core → runtimelib}/constructs.py +5 -8
  68. jaclang/{core → runtimelib}/context.py +86 -59
  69. jaclang/runtimelib/importer.py +361 -0
  70. jaclang/runtimelib/machine.py +158 -0
  71. jaclang/runtimelib/memory.py +158 -0
  72. jaclang/{core → runtimelib}/utils.py +30 -15
  73. jaclang/settings.py +5 -4
  74. jaclang/tests/fixtures/abc.jac +3 -3
  75. jaclang/tests/fixtures/access_checker.jac +12 -17
  76. jaclang/tests/fixtures/access_modifier.jac +88 -33
  77. jaclang/tests/fixtures/baddy.jac +3 -0
  78. jaclang/tests/fixtures/baddy.test.jac +3 -0
  79. jaclang/tests/fixtures/bar.jac +34 -0
  80. jaclang/tests/fixtures/byllmissue.jac +1 -5
  81. jaclang/tests/fixtures/chandra_bugs2.jac +11 -10
  82. jaclang/tests/fixtures/cls_method.jac +41 -0
  83. jaclang/tests/fixtures/dblhello.jac +6 -0
  84. jaclang/tests/fixtures/deep/one_lev.jac +3 -3
  85. jaclang/tests/fixtures/deep/one_lev_dup.jac +2 -3
  86. jaclang/tests/fixtures/deep_import_mods.jac +13 -0
  87. jaclang/tests/fixtures/edge_node_walk.jac +1 -1
  88. jaclang/tests/fixtures/edge_ops.jac +1 -1
  89. jaclang/tests/fixtures/edges_walk.jac +1 -1
  90. jaclang/tests/fixtures/err.impl.jac +3 -0
  91. jaclang/tests/fixtures/err.jac +4 -2
  92. jaclang/tests/fixtures/err_runtime.jac +15 -0
  93. jaclang/tests/fixtures/foo.jac +43 -0
  94. jaclang/tests/fixtures/gendot_bubble_sort.jac +1 -1
  95. jaclang/tests/fixtures/hello.jac +4 -0
  96. jaclang/tests/fixtures/impl_grab.impl.jac +2 -1
  97. jaclang/tests/fixtures/impl_grab.jac +4 -1
  98. jaclang/tests/fixtures/import.jac +9 -0
  99. jaclang/tests/fixtures/index_slice.jac +30 -0
  100. jaclang/tests/fixtures/jp_importer_auto.jac +14 -0
  101. jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
  102. jaclang/tests/fixtures/needs_import.jac +2 -2
  103. jaclang/tests/fixtures/pyfunc_1.py +1 -1
  104. jaclang/tests/fixtures/pyfunc_2.py +5 -2
  105. jaclang/tests/fixtures/pygame_mock/__init__.py +3 -0
  106. jaclang/tests/fixtures/pygame_mock/color.py +3 -0
  107. jaclang/tests/fixtures/pygame_mock/constants.py +5 -0
  108. jaclang/tests/fixtures/pygame_mock/display.py +2 -0
  109. jaclang/tests/fixtures/pygame_mock/inner/__init__.py +0 -0
  110. jaclang/tests/fixtures/pygame_mock/inner/iner_mod.py +2 -0
  111. jaclang/tests/fixtures/registry.jac +9 -0
  112. jaclang/tests/fixtures/run_test.jac +4 -4
  113. jaclang/tests/fixtures/semstr.jac +1 -4
  114. jaclang/tests/fixtures/simple_archs.jac +1 -1
  115. jaclang/tests/test_cli.py +109 -3
  116. jaclang/tests/test_language.py +170 -68
  117. jaclang/tests/test_reference.py +2 -3
  118. jaclang/utils/helpers.py +45 -21
  119. jaclang/utils/test.py +9 -0
  120. jaclang/utils/treeprinter.py +30 -7
  121. {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/METADATA +3 -2
  122. {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/RECORD +126 -90
  123. jaclang/core/architype.py +0 -502
  124. jaclang/core/importer.py +0 -344
  125. jaclang/core/memory.py +0 -99
  126. jaclang/tests/fixtures/aott_raise.jac +0 -25
  127. jaclang/tests/fixtures/package_import.jac +0 -6
  128. /jaclang/{core → runtimelib}/__init__.py +0 -0
  129. /jaclang/{core → runtimelib}/test.py +0 -0
  130. {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/WHEEL +0 -0
  131. {jaclang-0.7.14.dist-info → jaclang-0.7.17.dist-info}/entry_points.txt +0 -0
@@ -137,7 +137,7 @@ class SymbolTable:
137
137
  return k
138
138
  return None
139
139
 
140
- def push_scope(self, name: str, key_node: ast.AstNode) -> SymbolTable:
140
+ def push_kid_scope(self, name: str, key_node: ast.AstNode) -> SymbolTable:
141
141
  """Push a new scope onto the symbol table."""
142
142
  self.kid.append(SymbolTable(name, key_node, self))
143
143
  return self.kid[-1]
@@ -205,16 +205,21 @@ class SymbolTable:
205
205
  for i in node_list:
206
206
  if cur_sym_tab is None:
207
207
  break
208
- cur_sym_tab = (
209
- lookup.decl.sym_tab
210
- if (
211
- lookup := self.use_lookup(
212
- i,
213
- sym_table=cur_sym_tab,
214
- )
215
- )
216
- else None
217
- )
208
+ lookup = self.use_lookup(i, sym_table=cur_sym_tab)
209
+ if lookup:
210
+ cur_sym_tab = lookup.decl.sym_tab
211
+
212
+ # check if the symbol table name is not the same as symbol name
213
+ # then try to find a child scope with the same name
214
+ # This is used to get the scope in case of
215
+ # import:py math;
216
+ # b = math.floor(1.7);
217
+ if cur_sym_tab.name != i.sym_name:
218
+ t = cur_sym_tab.find_scope(i.sym_name)
219
+ if t:
220
+ cur_sym_tab = t
221
+ else:
222
+ cur_sym_tab = None
218
223
 
219
224
  def update_py_ctx_for_def(self, node: ast.AstSymbolNode) -> None:
220
225
  """Update python context for definition."""
@@ -5,26 +5,37 @@ import sys
5
5
 
6
6
  from jaclang import jac_import
7
7
  from jaclang.cli import cli
8
+ from jaclang.runtimelib.machine import JacMachine, JacProgram
8
9
  from jaclang.utils.test import TestCase
9
10
 
10
11
 
11
12
  class TestLoader(TestCase):
12
13
  """Test Jac self.prse."""
13
14
 
14
- def setUp(self) -> None:
15
- """Set up test."""
16
- return super().setUp()
17
-
18
15
  def test_import_basic_python(self) -> None:
19
16
  """Test basic self loading."""
17
+ JacMachine(self.fixture_abs_path(__file__)).attach_program(
18
+ JacProgram(mod_bundle=None, bytecode=None)
19
+ )
20
20
  (h,) = jac_import("fixtures.hello_world", base_path=__file__)
21
21
  self.assertEqual(h.hello(), "Hello World!") # type: ignore
22
+ JacMachine.detach()
22
23
 
23
24
  def test_modules_correct(self) -> None:
24
25
  """Test basic self loading."""
26
+ JacMachine(self.fixture_abs_path(__file__)).attach_program(
27
+ JacProgram(mod_bundle=None, bytecode=None)
28
+ )
25
29
  jac_import("fixtures.hello_world", base_path=__file__)
26
- self.assertIn("module 'hello_world'", str(sys.modules))
27
- self.assertIn("/tests/fixtures/hello_world.jac", str(sys.modules))
30
+ self.assertIn(
31
+ "module 'fixtures.hello_world'",
32
+ str(JacMachine.get().loaded_modules),
33
+ )
34
+ self.assertIn(
35
+ "/tests/fixtures/hello_world.jac",
36
+ str(JacMachine.get().loaded_modules),
37
+ )
38
+ JacMachine.detach()
28
39
 
29
40
  def test_jac_py_import(self) -> None:
30
41
  """Basic test for pass."""
@@ -39,11 +50,15 @@ class TestLoader(TestCase):
39
50
  stdout_value,
40
51
  )
41
52
 
42
- def test_package_import(self) -> None:
43
- """Test package import."""
53
+ def test_jac_py_import_auto(self) -> None:
54
+ """Basic test for pass."""
44
55
  captured_output = io.StringIO()
45
56
  sys.stdout = captured_output
46
- cli.run(self.fixture_abs_path("../../../tests/fixtures/package_import.jac"))
57
+ cli.run(self.fixture_abs_path("../../../tests/fixtures/jp_importer_auto.jac"))
47
58
  sys.stdout = sys.__stdout__
48
59
  stdout_value = captured_output.getvalue()
49
- self.assertEqual("package is imported successfully!\n", stdout_value)
60
+ self.assertIn("Hello World!", stdout_value)
61
+ self.assertIn(
62
+ "{SomeObj(a=10): 'check'} [MyObj(apple=5, banana=7), MyObj(apple=5, banana=7)]",
63
+ stdout_value,
64
+ )
@@ -4,25 +4,27 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  import logging
7
+ import time
7
8
  from concurrent.futures import ThreadPoolExecutor
8
9
  from typing import Callable, Optional
9
10
 
10
-
11
11
  import jaclang.compiler.absyntree as ast
12
12
  from jaclang.compiler.compile import jac_str_to_pass
13
13
  from jaclang.compiler.parser import JacParser
14
14
  from jaclang.compiler.passes import Pass
15
15
  from jaclang.compiler.passes.main.schedules import py_code_gen_typed
16
16
  from jaclang.compiler.passes.tool import FuseCommentsPass, JacFormatPass
17
+ from jaclang.langserve.sem_manager import SemTokManager
17
18
  from jaclang.langserve.utils import (
19
+ add_unique_text_edit,
18
20
  collect_all_symbols_in_scope,
19
- collect_symbols,
20
21
  create_range,
21
22
  find_deepest_symbol_node_at_pos,
23
+ find_index,
22
24
  gen_diagnostics,
23
25
  get_item_path,
24
26
  get_mod_path,
25
- locate_affected_token,
27
+ get_symbols_for_outline,
26
28
  parse_symbol_path,
27
29
  resolve_completion_symbol_table,
28
30
  )
@@ -43,104 +45,13 @@ class ModuleInfo:
43
45
  """Initialize module info."""
44
46
  self.ir = ir
45
47
  self.impl_parent: Optional[ModuleInfo] = impl_parent
46
- self.sem_tokens: list[int] = self.gen_sem_tokens()
48
+ self.sem_manager = SemTokManager(ir=ir)
47
49
 
48
50
  @property
49
51
  def uri(self) -> str:
50
52
  """Return uri."""
51
53
  return uris.from_fs_path(self.ir.loc.mod_path)
52
54
 
53
- def gen_sem_tokens(self) -> list[int]:
54
- """Return semantic tokens."""
55
- tokens = []
56
- prev_line, prev_col = 0, 0
57
- for node in self.ir._in_mod_nodes:
58
- if isinstance(node, ast.NameAtom) and node.sem_token:
59
- line, col_start, col_end = (
60
- node.loc.first_line - 1,
61
- node.loc.col_start - 1,
62
- node.loc.col_end - 1,
63
- )
64
- length = col_end - col_start
65
- tokens += [
66
- line - prev_line,
67
- col_start if line != prev_line else col_start - prev_col,
68
- length,
69
- *node.sem_token,
70
- ]
71
- prev_line, prev_col = line, col_start
72
- return tokens
73
-
74
- def update_sem_tokens(
75
- self, content_changes: lspt.DidChangeTextDocumentParams
76
- ) -> list[int]:
77
- """Update semantic tokens on change."""
78
- for change in [
79
- x
80
- for x in content_changes.content_changes
81
- if isinstance(x, lspt.TextDocumentContentChangeEvent_Type1)
82
- ]:
83
- change_start_line = change.range.start.line
84
- change_start_char = change.range.start.character
85
- change_end_line = change.range.end.line
86
- change_end_char = change.range.end.character
87
-
88
- line_delta = change.text.count("\n") - (change_end_line - change_start_line)
89
- if line_delta == 0:
90
- char_delta = len(change.text) - (change_end_char - change_start_char)
91
- else:
92
- last_newline_index = change.text.rfind("\n")
93
- char_delta = (
94
- len(change.text)
95
- - last_newline_index
96
- - 1
97
- - change_end_char
98
- + change_start_char
99
- )
100
-
101
- changed_token_index = locate_affected_token(
102
- self.sem_tokens,
103
- change_start_line,
104
- change_start_char,
105
- change_end_line,
106
- change_end_char,
107
- )
108
- if changed_token_index:
109
- self.sem_tokens[changed_token_index + 2] = max(
110
- 1, self.sem_tokens[changed_token_index + 2] + char_delta
111
- )
112
- if (
113
- len(self.sem_tokens) > changed_token_index + 5
114
- and self.sem_tokens[changed_token_index + 5] == 0
115
- ):
116
- next_token_index = changed_token_index + 5
117
- self.sem_tokens[next_token_index + 1] = max(
118
- 0, self.sem_tokens[next_token_index + 1] + char_delta
119
- )
120
- return self.sem_tokens
121
-
122
- current_token_index = 0
123
- line_offset = 0
124
- while current_token_index < len(self.sem_tokens):
125
- token_line_number = self.sem_tokens[current_token_index] + line_offset
126
- token_start_pos = self.sem_tokens[current_token_index + 1]
127
-
128
- if token_line_number > change_start_line or (
129
- token_line_number == change_start_line
130
- and token_start_pos >= change_start_char
131
- ):
132
- self.sem_tokens[current_token_index] += line_delta
133
- if token_line_number == change_start_line:
134
- self.sem_tokens[current_token_index + 1] += char_delta
135
- if token_line_number > change_end_line or (
136
- token_line_number == change_end_line
137
- and token_start_pos >= change_end_char
138
- ):
139
- break
140
- line_offset += self.sem_tokens[current_token_index]
141
- current_token_index += 5
142
- return self.sem_tokens
143
-
144
55
 
145
56
  class JacLangServer(LanguageServer):
146
57
  """Class for managing workspace."""
@@ -190,6 +101,7 @@ class JacLangServer(LanguageServer):
190
101
  def deep_check(self, file_path: str, annex_view: Optional[str] = None) -> bool:
191
102
  """Rebuild a file and its dependencies."""
192
103
  try:
104
+ start_time = time.time()
193
105
  document = self.workspace.get_text_document(file_path)
194
106
  if file_path in self.modules and (
195
107
  parent := self.modules[file_path].impl_parent
@@ -209,13 +121,23 @@ class JacLangServer(LanguageServer):
209
121
  )
210
122
 
211
123
  self.publish_diagnostics(
212
- file_path,
124
+ annex_view if annex_view else file_path,
213
125
  gen_diagnostics(
214
126
  annex_view if annex_view else file_path,
215
127
  build.errors_had,
216
128
  build.warnings_had,
217
129
  ),
218
130
  )
131
+ if annex_view:
132
+ self.publish_diagnostics(
133
+ file_path,
134
+ gen_diagnostics(
135
+ file_path,
136
+ build.errors_had,
137
+ build.warnings_had,
138
+ ),
139
+ )
140
+ self.log_py(f"PROFILE: Deep check took {time.time() - start_time} seconds.")
219
141
  return len(build.errors_had) == 0
220
142
  except Exception as e:
221
143
  self.log_error(f"Error during deep check: {e}")
@@ -256,12 +178,12 @@ class JacLangServer(LanguageServer):
256
178
  current_line = document.lines[position.line]
257
179
  current_pos = position.character
258
180
  current_symbol_path = parse_symbol_path(current_line, current_pos)
181
+
259
182
  node_selected = find_deepest_symbol_node_at_pos(
260
183
  self.modules[file_path].ir,
261
184
  position.line,
262
185
  position.character - 2,
263
186
  )
264
-
265
187
  mod_tab = (
266
188
  self.modules[file_path].ir.sym_tab
267
189
  if not node_selected
@@ -269,15 +191,30 @@ class JacLangServer(LanguageServer):
269
191
  )
270
192
  current_tab = self.modules[file_path].ir._sym_tab
271
193
  current_symbol_table = mod_tab
194
+
272
195
  if completion_trigger == ".":
273
- completion_items = resolve_completion_symbol_table(
274
- mod_tab, current_symbol_path, current_tab
275
- )
196
+ if current_symbol_path:
197
+ completion_items = resolve_completion_symbol_table(
198
+ mod_tab, current_symbol_path, current_tab
199
+ )
200
+ else:
201
+ completion_items = []
276
202
  else:
277
- try: # noqa SIM105
278
- completion_items = collect_all_symbols_in_scope(current_symbol_table)
279
- except AttributeError:
280
- pass
203
+ if node_selected and (
204
+ node_selected.find_parent_of_type(ast.Architype)
205
+ or node_selected.find_parent_of_type(ast.AbilityDef)
206
+ ):
207
+ self_symbol = [
208
+ lspt.CompletionItem(
209
+ label="self", kind=lspt.CompletionItemKind.Variable
210
+ )
211
+ ]
212
+ else:
213
+ self_symbol = []
214
+
215
+ completion_items = (
216
+ collect_all_symbols_in_scope(current_symbol_table) + self_symbol
217
+ )
281
218
  return lspt.CompletionList(is_incomplete=False, items=completion_items)
282
219
 
283
220
  def rename_module(self, old_path: str, new_path: str) -> None:
@@ -327,9 +264,16 @@ class JacLangServer(LanguageServer):
327
264
  """Return hover information for a file."""
328
265
  if file_path not in self.modules:
329
266
  return None
330
- node_selected = find_deepest_symbol_node_at_pos(
331
- self.modules[file_path].ir, position.line, position.character
267
+ token_index = find_index(
268
+ self.modules[file_path].sem_manager.sem_tokens,
269
+ position.line,
270
+ position.character,
332
271
  )
272
+ if token_index is None:
273
+ return None
274
+ node_selected = self.modules[file_path].sem_manager.static_sem_tokens[
275
+ token_index
276
+ ][3]
333
277
  value = self.get_node_info(node_selected) if node_selected else None
334
278
  if value:
335
279
  return lspt.Hover(
@@ -361,12 +305,12 @@ class JacLangServer(LanguageServer):
361
305
  self.log_warning(f"Attribute error when accessing node attributes: {e}")
362
306
  return node_info.strip()
363
307
 
364
- def get_document_symbols(self, file_path: str) -> list[lspt.DocumentSymbol]:
308
+ def get_outline(self, file_path: str) -> list[lspt.DocumentSymbol]:
365
309
  """Return document symbols for a file."""
366
310
  if file_path in self.modules and (
367
311
  root_node := self.modules[file_path].ir._sym_tab
368
312
  ):
369
- return collect_symbols(root_node)
313
+ return get_symbols_for_outline(root_node)
370
314
  return []
371
315
 
372
316
  def get_definition(
@@ -375,9 +319,16 @@ class JacLangServer(LanguageServer):
375
319
  """Return definition location for a file."""
376
320
  if file_path not in self.modules:
377
321
  return None
378
- node_selected: Optional[ast.AstSymbolNode] = find_deepest_symbol_node_at_pos(
379
- self.modules[file_path].ir, position.line, position.character
322
+ token_index = find_index(
323
+ self.modules[file_path].sem_manager.sem_tokens,
324
+ position.line,
325
+ position.character,
380
326
  )
327
+ if token_index is None:
328
+ return None
329
+ node_selected = self.modules[file_path].sem_manager.static_sem_tokens[
330
+ token_index
331
+ ][3]
381
332
  if node_selected:
382
333
  if (
383
334
  isinstance(node_selected, ast.Name)
@@ -400,13 +351,13 @@ class JacLangServer(LanguageServer):
400
351
  ):
401
352
  path_range = get_item_path(node_selected.parent)
402
353
  if path_range:
403
- path, range = path_range
404
- if path and range:
354
+ path, loc_range = path_range
355
+ if path and loc_range:
405
356
  return lspt.Location(
406
357
  uri=uris.from_fs_path(path),
407
358
  range=lspt.Range(
408
- start=lspt.Position(line=range[0], character=0),
409
- end=lspt.Position(line=range[1], character=5),
359
+ start=lspt.Position(line=loc_range[0], character=0),
360
+ end=lspt.Position(line=loc_range[1], character=5),
410
361
  ),
411
362
  )
412
363
  else:
@@ -424,7 +375,6 @@ class JacLangServer(LanguageServer):
424
375
  else node_selected
425
376
  )
426
377
  )
427
- self.log_py(f"{node_selected}, {decl_node}")
428
378
  decl_uri = uris.from_fs_path(decl_node.loc.mod_path)
429
379
  try:
430
380
  decl_range = create_range(decl_node.loc)
@@ -443,9 +393,16 @@ class JacLangServer(LanguageServer):
443
393
  self, file_path: str, position: lspt.Position
444
394
  ) -> list[lspt.Location]:
445
395
  """Return references for a file."""
446
- node_selected = find_deepest_symbol_node_at_pos(
447
- self.modules[file_path].ir, position.line, position.character
396
+ if file_path not in self.modules:
397
+ return []
398
+ index1 = find_index(
399
+ self.modules[file_path].sem_manager.sem_tokens,
400
+ position.line,
401
+ position.character,
448
402
  )
403
+ if index1 is None:
404
+ return []
405
+ node_selected = self.modules[file_path].sem_manager.static_sem_tokens[index1][3]
449
406
  if node_selected and node_selected.sym:
450
407
  list_of_references: list[lspt.Location] = [
451
408
  lspt.Location(
@@ -457,11 +414,40 @@ class JacLangServer(LanguageServer):
457
414
  return list_of_references
458
415
  return []
459
416
 
417
+ def rename_symbol(
418
+ self, file_path: str, position: lspt.Position, new_name: str
419
+ ) -> Optional[lspt.WorkspaceEdit]:
420
+ """Rename a symbol in a file."""
421
+ if file_path not in self.modules:
422
+ return None
423
+ index1 = find_index(
424
+ self.modules[file_path].sem_manager.sem_tokens,
425
+ position.line,
426
+ position.character,
427
+ )
428
+ if index1 is None:
429
+ return None
430
+ node_selected = self.modules[file_path].sem_manager.static_sem_tokens[index1][3]
431
+ if node_selected and node_selected.sym:
432
+ changes: dict[str, list[lspt.TextEdit]] = {}
433
+ for node in [
434
+ *node_selected.sym.uses,
435
+ node_selected.sym.defn[0],
436
+ ]:
437
+ key = uris.from_fs_path(node.loc.mod_path)
438
+ new_edit = lspt.TextEdit(
439
+ range=create_range(node.loc),
440
+ new_text=new_name,
441
+ )
442
+ add_unique_text_edit(changes, key, new_edit)
443
+ return lspt.WorkspaceEdit(changes=changes)
444
+ return None
445
+
460
446
  def get_semantic_tokens(self, file_path: str) -> lspt.SemanticTokens:
461
447
  """Return semantic tokens for a file."""
462
448
  if file_path not in self.modules:
463
449
  return lspt.SemanticTokens(data=[])
464
- return lspt.SemanticTokens(data=self.modules[file_path].sem_tokens)
450
+ return lspt.SemanticTokens(data=self.modules[file_path].sem_manager.sem_tokens)
465
451
 
466
452
  def log_error(self, message: str) -> None:
467
453
  """Log an error message."""