jaclang 0.7.13__py3-none-any.whl → 0.7.15__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 (49) hide show
  1. jaclang/cli/cli.py +11 -8
  2. jaclang/cli/cmdreg.py +9 -12
  3. jaclang/compiler/__init__.py +0 -2
  4. jaclang/compiler/absyntree.py +12 -12
  5. jaclang/compiler/parser.py +4 -0
  6. jaclang/compiler/passes/ir_pass.py +3 -12
  7. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +4 -5
  8. jaclang/compiler/passes/main/import_pass.py +5 -2
  9. jaclang/compiler/passes/main/pyast_gen_pass.py +33 -39
  10. jaclang/compiler/passes/main/pyast_load_pass.py +1 -0
  11. jaclang/compiler/passes/main/registry_pass.py +1 -1
  12. jaclang/compiler/passes/main/tests/test_import_pass.py +5 -1
  13. jaclang/compiler/passes/tool/fuse_comments_pass.py +14 -2
  14. jaclang/compiler/passes/tool/jac_formatter_pass.py +23 -28
  15. jaclang/compiler/passes/transform.py +4 -0
  16. jaclang/compiler/semtable.py +5 -3
  17. jaclang/compiler/tests/test_importer.py +3 -0
  18. jaclang/langserve/engine.py +227 -94
  19. jaclang/langserve/server.py +6 -10
  20. jaclang/langserve/tests/fixtures/base_module_structure.jac +1 -1
  21. jaclang/langserve/tests/fixtures/import_include_statements.jac +1 -1
  22. jaclang/langserve/tests/test_sem_tokens.py +277 -0
  23. jaclang/langserve/tests/test_server.py +28 -4
  24. jaclang/langserve/utils.py +128 -95
  25. jaclang/plugin/builtin.py +1 -1
  26. jaclang/plugin/default.py +25 -16
  27. jaclang/plugin/feature.py +4 -5
  28. jaclang/plugin/spec.py +2 -2
  29. jaclang/{core → runtimelib}/architype.py +1 -1
  30. jaclang/{core → runtimelib}/context.py +4 -1
  31. jaclang/runtimelib/importer.py +414 -0
  32. jaclang/runtimelib/machine.py +19 -0
  33. jaclang/{core → runtimelib}/utils.py +1 -1
  34. jaclang/tests/fixtures/blankwithentry.jac +3 -0
  35. jaclang/tests/fixtures/deep/one_lev.jac +3 -3
  36. jaclang/tests/fixtures/deep/one_lev_dup.jac +2 -3
  37. jaclang/tests/test_cli.py +1 -1
  38. jaclang/tests/test_language.py +16 -0
  39. jaclang/tests/test_man_code.py +17 -0
  40. jaclang/utils/treeprinter.py +0 -4
  41. {jaclang-0.7.13.dist-info → jaclang-0.7.15.dist-info}/METADATA +1 -1
  42. {jaclang-0.7.13.dist-info → jaclang-0.7.15.dist-info}/RECORD +48 -45
  43. jaclang/core/importer.py +0 -344
  44. /jaclang/{core → runtimelib}/__init__.py +0 -0
  45. /jaclang/{core → runtimelib}/constructs.py +0 -0
  46. /jaclang/{core → runtimelib}/memory.py +0 -0
  47. /jaclang/{core → runtimelib}/test.py +0 -0
  48. {jaclang-0.7.13.dist-info → jaclang-0.7.15.dist-info}/WHEEL +0 -0
  49. {jaclang-0.7.13.dist-info → jaclang-0.7.15.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,277 @@
1
+ """Test Semantic Tokens Update."""
2
+
3
+ import copy
4
+ import lsprotocol.types as lspt
5
+
6
+ from jaclang.langserve.engine import ModuleInfo
7
+ from jaclang.utils.test import TestCase
8
+
9
+ from typing import Tuple
10
+
11
+
12
+ class TestUpdateSemTokens(TestCase):
13
+ """Test update semantic tokens"""
14
+
15
+ def setUp(self) -> None:
16
+ """Set up test."""
17
+ # fmt: off
18
+ self.initial_sem_tokens = [
19
+ 1, 10, 4, 0, 2, 3, 4, 14, 12, 1, 0, 15, 6, 7, 1, 0, 8, 5, 2, 1,
20
+ 0, 10, 5, 2, 1, 1, 11, 4, 0, 2, 0, 10, 6, 7, 1, 0, 9, 6, 7, 1
21
+ ]
22
+ # fmt: on
23
+
24
+ self.document_lines = [
25
+ "",
26
+ "import:py math;",
27
+ "",
28
+ '"""Function to calculate the area of a circle."""',
29
+ "can calculate_area(radius: float) -> float {",
30
+ " return math.pi * radius * radius;",
31
+ "}",
32
+ " ",
33
+ ]
34
+
35
+ def check_semantic_token_update(self, case: Tuple, expected_output: str) -> None:
36
+ """Check semantic token update."""
37
+ doc_lines = copy.deepcopy(self.document_lines)
38
+
39
+ updated_semtokens = ModuleInfo.update_sem_tokens(
40
+ "circle_ir",
41
+ lspt.DidChangeTextDocumentParams(
42
+ text_document=lspt.VersionedTextDocumentIdentifier(
43
+ version=32,
44
+ uri="...jaclang/examples/manual_code/circle.jac",
45
+ ),
46
+ content_changes=[
47
+ lspt.TextDocumentContentChangeEvent_Type1(
48
+ range=lspt.Range(start=case[0], end=case[1]),
49
+ text=case[2],
50
+ range_length=case[3],
51
+ )
52
+ ],
53
+ ),
54
+ sem_tokens=copy.deepcopy(self.initial_sem_tokens),
55
+ document_lines=doc_lines,
56
+ )
57
+ self.assertIn(
58
+ expected_output, str(updated_semtokens), f"\nFailed for case: {case[4]}"
59
+ )
60
+
61
+ def test_multiline_before_first_token(self) -> None:
62
+ """Test multiline before first token."""
63
+ case = (
64
+ lspt.Position(line=0, character=0),
65
+ lspt.Position(line=0, character=0),
66
+ "\n",
67
+ 0,
68
+ "Multiline before first token (Basic)",
69
+ )
70
+ self.document_lines.insert(1, "")
71
+ self.check_semantic_token_update(case, "2, 10, 4, 0, 2, 3, 4, 14,")
72
+
73
+ def test_multiline_between_tokens(self) -> None:
74
+ """Test multiline between tokens."""
75
+ case = (
76
+ lspt.Position(line=5, character=19),
77
+ lspt.Position(line=5, character=19),
78
+ "\n ",
79
+ 0,
80
+ "Multiline between tokens (Basic)",
81
+ )
82
+ self.document_lines[5] = " return math.pi "
83
+ self.document_lines.insert(6, " * radius * radius;")
84
+ self.check_semantic_token_update(case, "2, 1, 6, 6, 7, 1, ")
85
+
86
+ def test_multiline_at_end_of_line(self) -> None:
87
+ """Test multiline at end of line."""
88
+ case = (
89
+ lspt.Position(line=4, character=37),
90
+ lspt.Position(line=4, character=37),
91
+ "\n",
92
+ 0,
93
+ "Multiline at end of line",
94
+ )
95
+ self.document_lines[4] = "can calculate_area(radius: float) -> "
96
+ self.document_lines.insert(5, "float {")
97
+ self.check_semantic_token_update(case, " 2, 1, 1, 0, 5, 2, 1, ")
98
+
99
+ def test_sameline_space_between_tokens(self) -> None:
100
+ """Test sameline space between tokens."""
101
+ case = (
102
+ lspt.Position(line=5, character=20),
103
+ lspt.Position(line=5, character=20),
104
+ " ",
105
+ 0,
106
+ "Sameline space between tokens (Basic)",
107
+ )
108
+ self.check_semantic_token_update(case, "0, 11, 6, 7, 1,")
109
+
110
+ def test_sameline_tab_between_tokens(self) -> None:
111
+ """Test sameline tab between tokens."""
112
+ case = (
113
+ lspt.Position(line=5, character=20),
114
+ lspt.Position(line=5, character=20),
115
+ " ",
116
+ 0,
117
+ "Sameline tab between tokens (Basic)",
118
+ )
119
+ self.check_semantic_token_update(case, "0, 14, 6, 7, 1")
120
+
121
+ def test_tab_at_start_of_token(self) -> None:
122
+ """Test tab at start of token."""
123
+ case = (
124
+ lspt.Position(line=5, character=21),
125
+ lspt.Position(line=5, character=21),
126
+ " ",
127
+ 0,
128
+ "Tab at start of a token",
129
+ )
130
+ self.check_semantic_token_update(case, "0, 13, 6, 7, 1,")
131
+
132
+ def test_insert_inside_token(self) -> None:
133
+ """Test insert inside token."""
134
+ case = (
135
+ lspt.Position(line=5, character=13),
136
+ lspt.Position(line=5, character=13),
137
+ "calculate",
138
+ 0,
139
+ "insert inside a token",
140
+ )
141
+ self.check_semantic_token_update(case, "1, 11, 13, 0, 2, 0, 19, 6, 7, 1")
142
+
143
+ def test_insert_inside_token_selected_range(self) -> None:
144
+ """Test insert inside token selected range."""
145
+ case = (
146
+ lspt.Position(line=5, character=12),
147
+ lspt.Position(line=5, character=14),
148
+ "calculate",
149
+ 2,
150
+ "insert inside a token in a selected range",
151
+ )
152
+ self.check_semantic_token_update(case, "1, 11, 11, 0, 2, 0, 17, 6, 7, 1,")
153
+
154
+ def test_newline_at_start_of_token(self) -> None:
155
+ """Test newline at start of token."""
156
+ case = (
157
+ lspt.Position(line=5, character=21),
158
+ lspt.Position(line=5, character=21),
159
+ "\n ",
160
+ 0,
161
+ "Newline at start of a token",
162
+ )
163
+ self.document_lines[5] = " return math.pi * "
164
+ self.document_lines.insert(6, " radius * radius;")
165
+ self.check_semantic_token_update(case, "0, 2, 1, 4, 6, 7, 1, 0")
166
+
167
+ def test_newline_after_parenthesis(self) -> None:
168
+ """Test newline after parenthesis."""
169
+ case = (
170
+ lspt.Position(line=4, character=19),
171
+ lspt.Position(line=4, character=19),
172
+ "\n ",
173
+ 0,
174
+ "Newline after parenthesis",
175
+ )
176
+ self.document_lines[4] = "can calculate_area("
177
+ self.document_lines.insert(5, " radius: float) -> float {")
178
+ self.check_semantic_token_update(case, "12, 1, 1, 4, 6, 7, 1, 0, 8")
179
+
180
+ def test_insert_newline_at_end_of_token(self) -> None:
181
+ """Test insert newline at end of token."""
182
+ case = (
183
+ lspt.Position(line=5, character=27),
184
+ lspt.Position(line=5, character=27),
185
+ "\n ",
186
+ 0,
187
+ "Insert Newline at end of a token",
188
+ )
189
+ self.document_lines[5] = " return math.pi * radius"
190
+ self.document_lines.insert(6, " * radius;")
191
+ self.check_semantic_token_update(case, "7, 1, 1, 7, 6, 7")
192
+
193
+ def test_deletion_basic(self) -> None:
194
+ """Test deletion basic."""
195
+ case = (
196
+ lspt.Position(line=5, character=4),
197
+ lspt.Position(line=5, character=4),
198
+ "",
199
+ 4,
200
+ "Deletion Basic",
201
+ )
202
+ self.check_semantic_token_update(case, "0, 10, 5, 2, 1, 1, 7, 4, 0, 2, 0")
203
+
204
+ def test_multiline_deletion(self) -> None:
205
+ """Test multiline deletion."""
206
+ case = (
207
+ lspt.Position(line=3, character=49),
208
+ lspt.Position(line=4, character=0),
209
+ "",
210
+ 4,
211
+ "Multiline Deletion",
212
+ )
213
+ self.document_lines[3] = (
214
+ '"""Function to calculate the area of a circle."""can calculate_area(radius: float) -> float {'
215
+ )
216
+ del self.document_lines[4]
217
+ self.check_semantic_token_update(case, "2, 2, 53, 14, 12, 1, 0")
218
+
219
+ def test_single_deletion_inside_token(self) -> None:
220
+ """Test single deletion inside token."""
221
+ case = (
222
+ lspt.Position(line=5, character=12),
223
+ lspt.Position(line=5, character=13),
224
+ "",
225
+ 1,
226
+ "single Deletion inside token",
227
+ )
228
+ self.check_semantic_token_update(case, "1, 1, 11, 3, 0, 2, 0, 9, 6")
229
+
230
+ def test_deletion_inside_token_selected_range(self) -> None:
231
+ """Test deletion inside token selected range."""
232
+ case = (
233
+ lspt.Position(line=4, character=10),
234
+ lspt.Position(line=4, character=15),
235
+ "",
236
+ 5,
237
+ "Deletion inside token- selected range",
238
+ )
239
+ self.check_semantic_token_update(case, "4, 9, 12, 1, 0, 10, 6")
240
+
241
+ def test_selected_multiline_deletion(self) -> None:
242
+ """Test selected multiline deletion."""
243
+ case = (
244
+ lspt.Position(line=4, character=44),
245
+ lspt.Position(line=5, character=4),
246
+ "",
247
+ 5,
248
+ "selected Multi line Deletion",
249
+ )
250
+ self.document_lines[3] = (
251
+ "can calculate_area(radius: float) -> float {return math.pi * radius * radius;"
252
+ )
253
+ del self.document_lines[4]
254
+ self.check_semantic_token_update(case, "4, 0, 2, 3, 4, 14, 12, 1, 0, 15")
255
+
256
+ def test_multi_line_insert_on_selected_region(self) -> None:
257
+ """Test multi line insert on selected region."""
258
+ case = (
259
+ lspt.Position(line=4, character=26),
260
+ lspt.Position(line=4, character=27),
261
+ ':= a + a // 2) > 5 {\n print("b is grater than 5");\n }',
262
+ 1,
263
+ "multi line insert on selected region ",
264
+ )
265
+ self.document_lines = [
266
+ "",
267
+ "import:py math;",
268
+ "",
269
+ '"""Function to calculate the area of a circle."""',
270
+ "can calculate_area(radius::= a + a // 2) > 5 {",
271
+ ' print("b is grater than 5");',
272
+ " }float) -> float {",
273
+ " return math.pi * radius * radius;",
274
+ "}",
275
+ " ",
276
+ ]
277
+ self.check_semantic_token_update(case, " 2, 1, 2, 14, 5, 2, 1, 1, ")
@@ -1,7 +1,7 @@
1
1
  from jaclang.utils.test import TestCase
2
2
  from jaclang.vendor.pygls import uris
3
3
  from jaclang.vendor.pygls.workspace import Workspace
4
- from jaclang.langserve.engine import JacLangServer
4
+ from jaclang.langserve.engine import JacLangServer, ModuleInfo
5
5
  from .session import LspSession
6
6
 
7
7
  import lsprotocol.types as lspt
@@ -103,7 +103,7 @@ class TestJacLangServer(TestCase):
103
103
  lsp.lsp._workspace = workspace
104
104
  circle_file = uris.from_fs_path(self.fixture_abs_path("circle_pure.jac"))
105
105
  lsp.deep_check(circle_file)
106
- self.assertEqual(8, len(lsp.get_document_symbols(circle_file)))
106
+ self.assertEqual(8, len(lsp.get_outline(circle_file)))
107
107
 
108
108
  def test_go_to_definition(self) -> None:
109
109
  """Test that the go to definition is correct."""
@@ -119,7 +119,7 @@ class TestJacLangServer(TestCase):
119
119
  )
120
120
  self.assertIn(
121
121
  "fixtures/circle_pure.jac:13:11-13:16",
122
- str(lsp.get_definition(circle_file, lspt.Position(20, 17))),
122
+ str(lsp.get_definition(circle_file, lspt.Position(20, 16))),
123
123
  )
124
124
 
125
125
  def test_go_to_definition_method(self) -> None:
@@ -137,6 +137,30 @@ class TestJacLangServer(TestCase):
137
137
  str(lsp.get_definition(guess_game_file, lspt.Position(46, 45))),
138
138
  )
139
139
 
140
+ def test_go_to_definition_method_manual_impl(self) -> None:
141
+ """Test that the go to definition is correct."""
142
+ lsp = JacLangServer()
143
+ workspace_path = self.fixture_abs_path("")
144
+ workspace = Workspace(workspace_path, lsp)
145
+ lsp.lsp._workspace = workspace
146
+ decldef_file = uris.from_fs_path(
147
+ self.examples_abs_path("micro/decl_defs_impl.jac")
148
+ )
149
+ lsp.deep_check(decldef_file)
150
+ self.assertNotIn(
151
+ "decl_defs_main.jac:8:8-8:17",
152
+ str(lsp.get_definition(decldef_file, lspt.Position(2, 24))),
153
+ )
154
+ decldef_main_file = uris.from_fs_path(
155
+ self.examples_abs_path("micro/decl_defs_main.jac")
156
+ )
157
+ lsp.deep_check(decldef_main_file)
158
+ lsp.deep_check(decldef_file)
159
+ self.assertIn(
160
+ "decl_defs_main.jac:8:8-8:17",
161
+ str(lsp.get_definition(decldef_file, lspt.Position(2, 24))),
162
+ )
163
+
140
164
  def test_test_annex(self) -> None:
141
165
  """Test that the server doesn't run if there is a syntax error."""
142
166
  lsp = JacLangServer()
@@ -163,7 +187,7 @@ class TestJacLangServer(TestCase):
163
187
  )
164
188
  lsp.deep_check(import_file)
165
189
  positions = [
166
- (2, 16, "datetime.py:0:0-0:0"),
190
+ (2, 24, "datetime.py:0:0-0:0"),
167
191
  (3, 17, "base_module_structure.jac:0:0-0:0"),
168
192
  (3, 87, "base_module_structure.jac:23:0-23:5"),
169
193
  (5, 65, "py_import.py:12:0-20:5"),
@@ -89,53 +89,38 @@ def sym_tab_list(sym_tab: SymbolTable, file_path: str) -> list[SymbolTable]:
89
89
  return sym_tabs
90
90
 
91
91
 
92
- def find_deepest_symbol_node_at_pos(
93
- node: ast.AstNode, line: int, character: int
92
+ def find_node_by_position(
93
+ tokens: list[tuple[lspt.Position, int, int, ast.AstSymbolNode]],
94
+ line: int,
95
+ position: int,
94
96
  ) -> Optional[ast.AstSymbolNode]:
95
97
  """Return the deepest symbol node that contains the given position."""
96
- last_symbol_node = None
97
-
98
- if position_within_node(node, line, character):
99
- if isinstance(node, ast.AstSymbolNode):
100
- last_symbol_node = node
101
-
102
- for child in [i for i in node.kid if i.loc.mod_path == node.loc.mod_path]:
103
- if position_within_node(child, line, character):
104
- deeper_node = find_deepest_symbol_node_at_pos(child, line, character)
105
- if deeper_node is not None:
106
- last_symbol_node = deeper_node
107
-
108
- return last_symbol_node
98
+ for token in tokens:
99
+ pos, token_end, length, node = token
100
+ if pos.line == line and pos.character <= position < token_end:
101
+ return node
102
+ return None
109
103
 
110
104
 
111
- def position_within_node(node: ast.AstNode, line: int, character: int) -> bool:
112
- """Check if the position falls within the node's location."""
113
- if node.loc.first_line < line + 1 < node.loc.last_line:
114
- return True
115
- if (
116
- node.loc.first_line == line + 1
117
- and node.loc.col_start <= character + 1
118
- and (
119
- node.loc.last_line == line + 1
120
- and node.loc.col_end >= character + 1
121
- or node.loc.last_line > line + 1
122
- )
123
- ):
124
- return True
125
- if (
126
- node.loc.last_line == line + 1
127
- and node.loc.col_start <= character + 1 <= node.loc.col_end
105
+ def find_index(
106
+ sem_tokens: list[int],
107
+ line: int,
108
+ char: int,
109
+ ) -> Optional[int]:
110
+ """Find index."""
111
+ index = None
112
+ for i, j in enumerate(
113
+ [get_token_start(i, sem_tokens) for i in range(0, len(sem_tokens), 5)]
128
114
  ):
129
- return True
130
- return False
115
+ if j[0] == line and j[1] <= char <= j[2]:
116
+ return i
131
117
 
118
+ return index
132
119
 
133
- def collect_symbols(node: SymbolTable) -> list[lspt.DocumentSymbol]:
120
+
121
+ def get_symbols_for_outline(node: SymbolTable) -> list[lspt.DocumentSymbol]:
134
122
  """Recursively collect symbols from the AST."""
135
123
  symbols = []
136
- if node is None:
137
- return symbols
138
-
139
124
  for key, item in node.tab.items():
140
125
  if (
141
126
  key in dir(builtins)
@@ -143,23 +128,20 @@ def collect_symbols(node: SymbolTable) -> list[lspt.DocumentSymbol]:
143
128
  or item.decl.loc.mod_path != node.owner.loc.mod_path
144
129
  ):
145
130
  continue
146
- else:
147
-
148
- pos = create_range(item.decl.loc)
149
- symbol = lspt.DocumentSymbol(
150
- name=key,
151
- kind=kind_map(item.decl),
152
- range=pos,
153
- selection_range=pos,
154
- children=[],
155
- )
156
- symbols.append(symbol)
131
+ pos = create_range(item.decl.loc)
132
+ symbol = lspt.DocumentSymbol(
133
+ name=key,
134
+ kind=kind_map(item.decl),
135
+ range=pos,
136
+ selection_range=pos,
137
+ children=[],
138
+ )
139
+ symbols.append(symbol)
157
140
 
158
141
  for sub_tab in [
159
142
  i for i in node.kid if i.owner.loc.mod_path == node.owner.loc.mod_path
160
143
  ]:
161
- sub_symbols = collect_symbols(sub_tab)
162
-
144
+ sub_symbols = get_symbols_for_outline(sub_tab)
163
145
  if isinstance(
164
146
  sub_tab.owner,
165
147
  (ast.IfStmt, ast.ElseStmt, ast.WhileStmt, ast.IterForStmt, ast.InForStmt),
@@ -364,50 +346,6 @@ def get_definition_range(
364
346
  return None
365
347
 
366
348
 
367
- def locate_affected_token(
368
- tokens: list[int],
369
- change_start_line: int,
370
- change_start_char: int,
371
- change_end_line: int,
372
- change_end_char: int,
373
- ) -> Optional[int]:
374
- """Find in which token change is occurring."""
375
- token_index = 0
376
- current_line = 0
377
- line_char_offset = 0
378
-
379
- while token_index < len(tokens):
380
- token_line_delta = tokens[token_index]
381
- token_start_char = tokens[token_index + 1]
382
- token_length = tokens[token_index + 2]
383
-
384
- if token_line_delta > 0:
385
- current_line += token_line_delta
386
- line_char_offset = 0
387
- token_abs_start_char = line_char_offset + token_start_char
388
- token_abs_end_char = token_abs_start_char + token_length
389
- if (
390
- current_line == change_start_line == change_end_line
391
- and token_abs_start_char <= change_start_char
392
- and change_end_char <= token_abs_end_char
393
- ):
394
- return token_index
395
- if (
396
- current_line == change_start_line
397
- and token_abs_start_char <= change_start_char < token_abs_end_char
398
- ):
399
- return token_index
400
- if (
401
- current_line == change_end_line
402
- and token_abs_start_char < change_end_char <= token_abs_end_char
403
- ):
404
- return token_index
405
-
406
- line_char_offset += token_start_char
407
- token_index += 5
408
- return None
409
-
410
-
411
349
  def collect_all_symbols_in_scope(
412
350
  sym_tab: SymbolTable, up_tree: bool = True
413
351
  ) -> list[lspt.CompletionItem]:
@@ -558,3 +496,98 @@ def resolve_completion_symbol_table(
558
496
  collect_all_symbols_in_scope(current_symbol_table, up_tree=False)
559
497
  )
560
498
  return completion_items
499
+
500
+
501
+ def get_token_start(
502
+ token_index: int | None, sem_tokens: list[int]
503
+ ) -> tuple[int, int, int]:
504
+ """Return the starting position of a token."""
505
+ if token_index is None or token_index >= len(sem_tokens):
506
+ return 0, 0, 0
507
+
508
+ current_line = 0
509
+ current_char = 0
510
+ current_tok_index = 0
511
+
512
+ while current_tok_index < len(sem_tokens):
513
+ token_line_delta = sem_tokens[current_tok_index]
514
+ token_start_char = sem_tokens[current_tok_index + 1]
515
+
516
+ if token_line_delta > 0:
517
+ current_line += token_line_delta
518
+ current_char = 0
519
+ if current_tok_index == token_index:
520
+ if token_line_delta > 0:
521
+ return (
522
+ current_line,
523
+ token_start_char,
524
+ token_start_char + sem_tokens[current_tok_index + 2],
525
+ )
526
+ return (
527
+ current_line,
528
+ current_char + token_start_char,
529
+ current_char + token_start_char + sem_tokens[current_tok_index + 2],
530
+ )
531
+
532
+ current_char += token_start_char
533
+ current_tok_index += 5
534
+
535
+ return (
536
+ current_line,
537
+ current_char,
538
+ current_char + sem_tokens[current_tok_index + 2],
539
+ )
540
+
541
+
542
+ def find_surrounding_tokens(
543
+ change_start_line: int,
544
+ change_start_char: int,
545
+ change_end_line: int,
546
+ change_end_char: int,
547
+ sem_tokens: list[int],
548
+ ) -> tuple[int | None, int | None, bool]:
549
+ """Find the indices of the previous and next tokens surrounding the change."""
550
+ prev_token_index = None
551
+ next_token_index = None
552
+ inside_tok = False
553
+ for i, tok in enumerate(
554
+ [get_token_start(i, sem_tokens) for i in range(0, len(sem_tokens), 5)][0:]
555
+ ):
556
+ if (not (prev_token_index is None or next_token_index is None)) and (
557
+ tok[0] > change_end_line
558
+ or (tok[0] == change_end_line and tok[1] > change_end_char)
559
+ ):
560
+ prev_token_index = i * 5
561
+ break
562
+ elif (
563
+ change_start_line == tok[0] == change_end_line
564
+ and tok[1] <= change_start_char
565
+ and tok[2] >= change_end_char
566
+ ):
567
+ prev_token_index = i * 5
568
+ inside_tok = True
569
+ break
570
+ elif (tok[0] < change_start_line) or (
571
+ tok[0] == change_start_line and tok[1] < change_start_char
572
+ ):
573
+ prev_token_index = i * 5
574
+ elif (tok[0] > change_end_line) or (
575
+ tok[0] == change_end_line and tok[1] > change_end_char
576
+ ):
577
+ next_token_index = i * 5
578
+ break
579
+
580
+ return prev_token_index, next_token_index, inside_tok
581
+
582
+
583
+ def get_line_of_code(line_number: int, lines: list[str]) -> Optional[tuple[str, int]]:
584
+ """Get the line of code, and the first non-space character index."""
585
+ if 0 <= line_number < len(lines):
586
+ line = lines[line_number].rstrip("\n")
587
+ first_non_space = len(line) - len(line.lstrip())
588
+ return line, (
589
+ first_non_space + 4
590
+ if line.strip().endswith(("(", "{", "["))
591
+ else first_non_space
592
+ )
593
+ return None
jaclang/plugin/builtin.py CHANGED
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING
6
6
 
7
7
  if TYPE_CHECKING:
8
8
  from typing import Optional
9
- from jaclang.core.constructs import NodeArchitype
9
+ from jaclang.runtimelib.constructs import NodeArchitype
10
10
 
11
11
 
12
12
  def dotgen(
jaclang/plugin/default.py CHANGED
@@ -15,7 +15,7 @@ from typing import Any, Callable, Optional, Type, Union
15
15
  from jaclang.compiler.absyntree import Module
16
16
  from jaclang.compiler.constant import EdgeDir, colors
17
17
  from jaclang.compiler.semtable import SemInfo, SemRegistry, SemScope
18
- from jaclang.core.constructs import (
18
+ from jaclang.runtimelib.constructs import (
19
19
  Architype,
20
20
  DSFunc,
21
21
  EdgeAnchor,
@@ -32,9 +32,9 @@ from jaclang.core.constructs import (
32
32
  WalkerArchitype,
33
33
  exec_context,
34
34
  )
35
- from jaclang.core.importer import jac_importer
36
- from jaclang.core.utils import traverse_graph
37
- from jaclang.plugin.feature import JacFeature as Jac
35
+ from jaclang.runtimelib.importer import ImportPathSpec, JacImporter, PythonImporter
36
+ from jaclang.runtimelib.utils import traverse_graph
37
+ from jaclang.plugin.feature import JacFeature as Jac # noqa: I100
38
38
  from jaclang.plugin.spec import T
39
39
 
40
40
 
@@ -54,7 +54,6 @@ __all__ = [
54
54
  "WalkerArchitype",
55
55
  "Architype",
56
56
  "DSFunc",
57
- "jac_importer",
58
57
  "T",
59
58
  ]
60
59
 
@@ -216,18 +215,26 @@ class JacFeatureDefaults:
216
215
  items: Optional[dict[str, Union[str, Optional[str]]]],
217
216
  ) -> tuple[types.ModuleType, ...]:
218
217
  """Core Import Process."""
219
- result = jac_importer(
220
- target=target,
221
- base_path=base_path,
222
- absorb=absorb,
223
- cachable=cachable,
224
- mdl_alias=mdl_alias,
225
- override_name=override_name,
226
- mod_bundle=mod_bundle,
227
- lng=lng,
228
- items=items,
218
+ spec = ImportPathSpec(
219
+ target,
220
+ base_path,
221
+ absorb,
222
+ cachable,
223
+ mdl_alias,
224
+ override_name,
225
+ mod_bundle,
226
+ lng,
227
+ items,
228
+ )
229
+ if lng == "py":
230
+ import_result = PythonImporter(Jac.context().jac_machine).run_import(spec)
231
+ else:
232
+ import_result = JacImporter(Jac.context().jac_machine).run_import(spec)
233
+ return (
234
+ (import_result.ret_mod,)
235
+ if absorb or not items
236
+ else tuple(import_result.ret_items)
229
237
  )
230
- return result
231
238
 
232
239
  @staticmethod
233
240
  @hookimpl
@@ -260,6 +267,8 @@ class JacFeatureDefaults:
260
267
  base, mod_name = os.path.split(filepath)
261
268
  base = base if base else "./"
262
269
  mod_name = mod_name[:-4]
270
+ if mod_name.endswith(".test"):
271
+ mod_name = mod_name[:-5]
263
272
  JacTestCheck.reset()
264
273
  Jac.jac_import(target=mod_name, base_path=base)
265
274
  JacTestCheck.run_test(xit, maxfail, verbose)